summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet.rb1
-rw-r--r--lib/puppet/element.rb5
-rwxr-xr-xlib/puppet/loadedfile.rb1
-rw-r--r--lib/puppet/log.rb6
-rw-r--r--lib/puppet/parser/ast.rb266
-rw-r--r--lib/puppet/parser/ast/astarray.rb44
-rw-r--r--lib/puppet/parser/ast/classdef.rb77
-rw-r--r--lib/puppet/parser/ast/collection.rb97
-rw-r--r--lib/puppet/parser/ast/collexpr.rb81
-rw-r--r--lib/puppet/parser/ast/compdef.rb98
-rw-r--r--lib/puppet/parser/ast/component.rb198
-rw-r--r--lib/puppet/parser/ast/function.rb5
-rw-r--r--lib/puppet/parser/ast/hostclass.rb166
-rw-r--r--lib/puppet/parser/ast/leaf.rb23
-rw-r--r--lib/puppet/parser/ast/node.rb113
-rw-r--r--lib/puppet/parser/ast/nodedef.rb73
-rw-r--r--lib/puppet/parser/ast/objectdef.rb353
-rw-r--r--lib/puppet/parser/ast/objectref.rb77
-rw-r--r--lib/puppet/parser/ast/resourcedef.rb215
-rw-r--r--lib/puppet/parser/ast/resourcedefaults.rb (renamed from lib/puppet/parser/ast/typedefaults.rb)24
-rw-r--r--lib/puppet/parser/ast/resourceoverride.rb62
-rw-r--r--lib/puppet/parser/ast/resourceparam.rb (renamed from lib/puppet/parser/ast/objectparam.rb)21
-rw-r--r--lib/puppet/parser/ast/resourceref.rb44
-rw-r--r--lib/puppet/parser/ast/selector.rb6
-rw-r--r--lib/puppet/parser/ast/vardef.rb14
-rw-r--r--lib/puppet/parser/collector.rb97
-rw-r--r--lib/puppet/parser/functions.rb46
-rw-r--r--lib/puppet/parser/grammar.ra485
-rw-r--r--lib/puppet/parser/interpreter.rb1059
-rw-r--r--lib/puppet/parser/lexer.rb96
-rw-r--r--lib/puppet/parser/parser.rb1571
-rw-r--r--lib/puppet/parser/resource.rb324
-rw-r--r--lib/puppet/parser/resource/param.rb44
-rw-r--r--lib/puppet/parser/resource/reference.rb68
-rw-r--r--lib/puppet/parser/scope.rb1544
-rw-r--r--lib/puppet/parser/templatewrapper.rb58
-rw-r--r--lib/puppet/rails/database.rb17
-rw-r--r--lib/puppet/rails/host.rb54
-rw-r--r--lib/puppet/rails/rails_object.rb42
-rw-r--r--lib/puppet/rails/rails_parameter.rb10
-rw-r--r--lib/puppet/rails/rails_resource.rb34
-rw-r--r--lib/puppet/transportable.rb14
-rw-r--r--lib/puppet/type.rb31
-rw-r--r--lib/puppet/util.rb2
-rw-r--r--lib/puppet/util/errors.rb55
-rw-r--r--lib/puppet/util/logging.rb20
-rw-r--r--lib/puppet/util/methodhelper.rb21
-rw-r--r--test/data/failers/badclassnoparam (renamed from examples/code/failers/badclassnoparam)0
-rw-r--r--test/data/failers/badclassparam (renamed from examples/code/failers/badclassparam)0
-rw-r--r--test/data/failers/badcompnoparam (renamed from examples/code/failers/badcompnoparam)0
-rw-r--r--test/data/failers/badcompparam (renamed from examples/code/failers/badcompparam)0
-rw-r--r--test/data/failers/badtypeparam (renamed from examples/code/failers/badtypeparam)0
-rw-r--r--test/data/failers/noobjectrvalue (renamed from examples/code/failers/noobjectrvalue)0
-rw-r--r--test/data/snippets/aliastest.pp (renamed from examples/code/snippets/aliastest.pp)0
-rw-r--r--test/data/snippets/argumentdefaults (renamed from examples/code/snippets/argumentdefaults)0
-rw-r--r--test/data/snippets/casestatement.pp (renamed from examples/code/snippets/casestatement.pp)0
-rw-r--r--test/data/snippets/classheirarchy.pp (renamed from examples/code/snippets/classheirarchy.pp)0
-rw-r--r--test/data/snippets/classincludes.pp (renamed from examples/code/snippets/classincludes.pp)0
-rw-r--r--test/data/snippets/classpathtest (renamed from examples/code/snippets/classpathtest)0
-rw-r--r--test/data/snippets/componentmetaparams.pp (renamed from examples/code/snippets/componentmetaparams.pp)2
-rw-r--r--test/data/snippets/deepclassheirarchy.pp (renamed from examples/code/snippets/deepclassheirarchy.pp)0
-rw-r--r--test/data/snippets/defineoverrides.pp (renamed from examples/code/snippets/defineoverrides.pp)2
-rw-r--r--test/data/snippets/dirchmod (renamed from examples/code/snippets/dirchmod)0
-rw-r--r--test/data/snippets/emptyclass.pp (renamed from examples/code/snippets/emptyclass.pp)0
-rw-r--r--test/data/snippets/emptyexec.pp (renamed from examples/code/snippets/emptyexec.pp)0
-rw-r--r--test/data/snippets/failmissingexecpath.pp (renamed from examples/code/snippets/failmissingexecpath.pp)4
-rw-r--r--test/data/snippets/falsevalues.pp (renamed from examples/code/snippets/falsevalues.pp)0
-rw-r--r--test/data/snippets/filecreate (renamed from examples/code/snippets/filecreate)0
-rw-r--r--test/data/snippets/implicititeration (renamed from examples/code/snippets/implicititeration)0
-rw-r--r--test/data/snippets/multipleinstances (renamed from examples/code/snippets/multipleinstances)0
-rw-r--r--test/data/snippets/multisubs.pp13
-rw-r--r--test/data/snippets/namevartest (renamed from examples/code/snippets/namevartest)0
-rw-r--r--test/data/snippets/scopetest (renamed from examples/code/snippets/scopetest)4
-rw-r--r--test/data/snippets/selectorvalues.pp (renamed from examples/code/snippets/selectorvalues.pp)0
-rw-r--r--test/data/snippets/simpledefaults (renamed from examples/code/snippets/simpledefaults)0
-rw-r--r--test/data/snippets/simpleselector (renamed from examples/code/snippets/simpleselector)0
-rw-r--r--test/data/snippets/singleary.pp (renamed from examples/code/snippets/singleary.pp)4
-rw-r--r--test/data/snippets/singlequote.pp (renamed from examples/code/snippets/singlequote.pp)0
-rw-r--r--test/data/snippets/singleselector.pp (renamed from examples/code/snippets/singleselector.pp)0
-rw-r--r--test/data/snippets/tag.pp (renamed from examples/code/snippets/tag.pp)0
-rw-r--r--test/data/snippets/tagged.pp (renamed from examples/code/snippets/tagged.pp)4
-rwxr-xr-xtest/executables/puppetmodule.rb7
-rwxr-xr-xtest/language/ast.rb1135
-rwxr-xr-xtest/language/collector.rb206
-rwxr-xr-xtest/language/functions.rb16
-rwxr-xr-xtest/language/interpreter.rb709
-rw-r--r--test/language/lexer.rb78
-rw-r--r--test/language/node.rb72
-rw-r--r--test/language/parser.rb377
-rwxr-xr-xtest/language/rails.rb98
-rwxr-xr-xtest/language/resource.rb391
-rwxr-xr-xtest/language/scope.rb760
-rwxr-xr-xtest/language/snippets.rb9
-rwxr-xr-xtest/language/transportable.rb1
-rw-r--r--test/lib/puppettest.rb31
-rw-r--r--test/lib/puppettest/parsertesting.rb151
-rw-r--r--test/lib/puppettest/railstesting.rb34
-rw-r--r--test/lib/puppettest/resourcetesting.rb64
-rw-r--r--test/lib/puppettest/support/utils.rb4
-rwxr-xr-xtest/other/config.rb23
-rwxr-xr-xtest/rails/rails.rb87
-rwxr-xr-xtest/rails/railsparameter.rb50
-rwxr-xr-xtest/rails/railsresource.rb60
-rw-r--r--test/tagging/tagging.rb35
104 files changed, 6517 insertions, 5676 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb
index a802688e8..b0b3bbea9 100644
--- a/lib/puppet.rb
+++ b/lib/puppet.rb
@@ -1,5 +1,6 @@
# see the bottom of the file for further inclusions
require 'singleton'
+require 'facter'
require 'puppet/error'
require 'puppet/event-loop'
require 'puppet/util'
diff --git a/lib/puppet/element.rb b/lib/puppet/element.rb
index cc682f2e4..934fafeb9 100644
--- a/lib/puppet/element.rb
+++ b/lib/puppet/element.rb
@@ -7,6 +7,7 @@ require 'puppet'
class Puppet::Element
include Puppet
include Puppet::Util
+ include Puppet::Util::Errors
attr_writer :noop
class << self
@@ -48,10 +49,10 @@ class Puppet::Element
@path = [@parent.path, self.class.name.to_s + "=" + self.name]
end
else
- # The top-level name is always puppet[top], so we don't bother with
+ # The top-level name is always main[top], so we don't bother with
# that. And we don't add the hostname here, it gets added
# in the log server thingy.
- if self.name == "puppet[top]"
+ if self.name == "main[top]"
@path = ["/"]
else
if self.is_a?(Puppet.type(:component))
diff --git a/lib/puppet/loadedfile.rb b/lib/puppet/loadedfile.rb
index b0e408475..c14bcc195 100755
--- a/lib/puppet/loadedfile.rb
+++ b/lib/puppet/loadedfile.rb
@@ -44,6 +44,7 @@ module Puppet
"Can not use a non-existent file for parsing"
end
@statted = 0
+ @stamp = nil
@tstamp = stamp()
end
diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb
index 0659042ce..c8d92e649 100644
--- a/lib/puppet/log.rb
+++ b/lib/puppet/log.rb
@@ -489,10 +489,8 @@ module Puppet
# If they pass a source in to us, we make sure it is a string, and
# we retrieve any tags we can.
def source=(source)
- # We can't store the actual source, we just store the path. This
- # is a bit of a stupid hack, specifically testing for elements, but
- # eh.
- if source.is_a?(Puppet::Element) and source.respond_to?(:path)
+ # We can't store the actual source, we just store the path.
+ if source.respond_to?(:path)
@objectsource = true
@source = source.path
else
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
index ccde7928b..6f9c9492c 100644
--- a/lib/puppet/parser/ast.rb
+++ b/lib/puppet/parser/ast.rb
@@ -3,158 +3,126 @@
require 'puppet'
require 'puppet/autoload'
-module Puppet
- module Parser
- # The base class for all of the objects that make up the parse trees.
- # Handles things like file name, line #, and also does the initialization
- # for all of the parameters of all of the child objects.
- class AST
- # Do this so I don't have to type the full path in all of the subclasses
- AST = Puppet::Parser::AST
-
- Puppet.setdefaults("ast",
- :typecheck => [true, "Whether to validate types during parsing."],
- :paramcheck => [true, "Whether to validate parameters during parsing."]
- )
- attr_accessor :line, :file, :parent, :scope
-
- # Just used for 'tree', which is only used in debugging.
- @@pink = ""
- @@green = ""
- @@yellow = ""
- @@slate = ""
- @@reset = ""
-
- # Just used for 'tree', which is only used in debugging.
- @@indent = " " * 4
- @@indline = @@pink + ("-" * 4) + @@reset
- @@midline = @@slate + ("-" * 4) + @@reset
-
- @@settypes = {}
-
- # Just used for 'tree', which is only used in debugging.
- def AST.indention
- return @@indent * @@indention
- end
-
- # Just used for 'tree', which is only used in debugging.
- def AST.midline
- return @@midline
- end
-
- # Evaluate the current object. Basically just iterates across all
- # of the contained children and evaluates them in turn, returning a
- # list of all of the collected values, rejecting nil values
- def evaluate(args)
- #Puppet.debug("Evaluating ast %s" % @name)
- value = self.collect { |obj|
- obj.safeevaluate(args)
- }.reject { |obj|
- obj.nil?
- }
- end
-
- # The version of the evaluate method that should be called, because it
- # correctly handles errors. It is critical to use this method because
- # it can enable you to catch the error where it happens, rather than
- # much higher up the stack.
- def safeevaluate(*args)
- begin
- self.evaluate(*args)
- rescue Puppet::DevError => except
- except.line ||= @line
- except.file ||= @file
- raise
- rescue Puppet::ParseError => except
- except.line ||= @line
- except.file ||= @file
- raise
- rescue => detail
- error = Puppet::DevError.new(
- "Child of type %s failed with error %s: %s" %
- [self.class, detail.class, detail.to_s]
- )
- error.line ||= @line
- error.file ||= @file
- error.set_backtrace detail.backtrace
- raise error
- end
- end
-
- # Again, just used for printing out the parse tree.
- def typewrap(string)
- #return self.class.to_s.sub(/.+::/,'') +
- #"(" + @@green + string.to_s + @@reset + ")"
- return @@green + string.to_s + @@reset +
- "(" + self.class.to_s.sub(/.+::/,'') + ")"
- end
-
- # Initialize the object. Requires a hash as the argument, and
- # takes each of the parameters of the hash and calls the settor
- # method for them. This is probably pretty inefficient and should
- # likely be changed at some point.
- def initialize(args)
- @file = nil
- @line = nil
- args.each { |param,value|
- method = param.to_s + "="
- unless self.respond_to?(method)
- error = Puppet::ParseError.new(
- "Invalid parameter %s to object class %s" %
- [param,self.class.to_s]
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
-
- begin
- #Puppet.debug("sending %s to %s" % [method, self.class])
- self.send(method,value)
- rescue => detail
- error = Puppet::DevError.new(
- "Could not set parameter %s on class %s: %s" %
- [method,self.class.to_s,detail]
- )
- error.line ||= self.line
- error.file ||= self.file
- raise error
- end
- }
- end
- #---------------------------------------------------------------
- # Now autoload everything.
- # XXX We can't do this, because it causes multiple loads of some
- # things.
- #@autoloader = Puppet::Autoload.new(self,
- # "puppet/parser/ast"
- #)
- #@autoloader.loadall
+# The base class for all of the objects that make up the parse trees.
+# Handles things like file name, line #, and also does the initialization
+# for all of the parameters of all of the child objects.
+class Puppet::Parser::AST
+ # Do this so I don't have to type the full path in all of the subclasses
+ AST = Puppet::Parser::AST
+
+ include Puppet::Util::Errors
+ include Puppet::Util::MethodHelper
+
+ Puppet.setdefaults("ast",
+ :typecheck => [true, "Whether to validate types during parsing."],
+ :paramcheck => [true, "Whether to validate parameters during parsing."]
+ )
+ attr_accessor :line, :file, :parent, :scope
+
+ # Just used for 'tree', which is only used in debugging.
+ @@pink = ""
+ @@green = ""
+ @@yellow = ""
+ @@slate = ""
+ @@reset = ""
+
+ # Just used for 'tree', which is only used in debugging.
+ @@indent = " " * 4
+ @@indline = @@pink + ("-" * 4) + @@reset
+ @@midline = @@slate + ("-" * 4) + @@reset
+
+ @@settypes = {}
+
+ # Just used for 'tree', which is only used in debugging.
+ def AST.indention
+ return @@indent * @@indention
+ end
+
+ # Just used for 'tree', which is only used in debugging.
+ def AST.midline
+ return @@midline
+ end
+
+ # Evaluate the current object. Basically just iterates across all
+ # of the contained children and evaluates them in turn, returning a
+ # list of all of the collected values, rejecting nil values
+ def evaluate(args)
+ #Puppet.debug("Evaluating ast %s" % @name)
+ value = self.collect { |obj|
+ obj.safeevaluate(args)
+ }.reject { |obj|
+ obj.nil?
+ }
+ end
+
+ # Throw a parse error.
+ def parsefail(message)
+ self.fail(Puppet::ParseError, message)
+ end
+
+ # Wrap a statemp in a reusable way so we always throw a parse error.
+ def parsewrap
+ exceptwrap :type => Puppet::ParseError do
+ yield
+ end
+ end
+
+ # The version of the evaluate method that should be called, because it
+ # correctly handles errors. It is critical to use this method because
+ # it can enable you to catch the error where it happens, rather than
+ # much higher up the stack.
+ def safeevaluate(*args)
+ exceptwrap do
+ self.evaluate(*args)
end
end
+
+ # Again, just used for printing out the parse tree.
+ def typewrap(string)
+ #return self.class.to_s.sub(/.+::/,'') +
+ #"(" + @@green + string.to_s + @@reset + ")"
+ return @@green + string.to_s + @@reset +
+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
+ end
+
+ # Initialize the object. Requires a hash as the argument, and
+ # takes each of the parameters of the hash and calls the settor
+ # method for them. This is probably pretty inefficient and should
+ # likely be changed at some point.
+ def initialize(args)
+ @file = nil
+ @line = nil
+ set_options(args)
+ end
+ #---------------------------------------------------------------
+ # Now autoload everything.
+ # XXX We can't do this, because it causes multiple loads of some
+ # things.
+ @autoloader = Puppet::Autoload.new(self,
+ "puppet/parser/ast"
+ )
+ @autoloader.loadall
end
-require 'puppet/parser/ast/astarray'
-require 'puppet/parser/ast/branch'
-require 'puppet/parser/ast/collection'
-require 'puppet/parser/ast/caseopt'
-require 'puppet/parser/ast/casestatement'
-require 'puppet/parser/ast/classdef'
-require 'puppet/parser/ast/compdef'
-require 'puppet/parser/ast/component'
-require 'puppet/parser/ast/else'
-require 'puppet/parser/ast/hostclass'
-require 'puppet/parser/ast/ifstatement'
-require 'puppet/parser/ast/leaf'
-require 'puppet/parser/ast/node'
-require 'puppet/parser/ast/nodedef'
-require 'puppet/parser/ast/objectdef'
-require 'puppet/parser/ast/objectparam'
-require 'puppet/parser/ast/objectref'
-require 'puppet/parser/ast/selector'
-require 'puppet/parser/ast/typedefaults'
-require 'puppet/parser/ast/vardef'
-require 'puppet/parser/ast/tag'
-require 'puppet/parser/ast/function'
+#require 'puppet/parser/ast/astarray'
+#require 'puppet/parser/ast/branch'
+#require 'puppet/parser/ast/collection'
+#require 'puppet/parser/ast/caseopt'
+#require 'puppet/parser/ast/casestatement'
+#require 'puppet/parser/ast/component'
+#require 'puppet/parser/ast/else'
+#require 'puppet/parser/ast/hostclass'
+#require 'puppet/parser/ast/ifstatement'
+#require 'puppet/parser/ast/leaf'
+#require 'puppet/parser/ast/node'
+#require 'puppet/parser/ast/resourcedef'
+#require 'puppet/parser/ast/resourceparam'
+#require 'puppet/parser/ast/resourceref'
+#require 'puppet/parser/ast/resourceoverride'
+#require 'puppet/parser/ast/selector'
+#require 'puppet/parser/ast/resourcedefaults'
+#require 'puppet/parser/ast/vardef'
+#require 'puppet/parser/ast/tag'
+#require 'puppet/parser/ast/function'
# $Id$
diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb
index c42f658fd..fb0a3f671 100644
--- a/lib/puppet/parser/ast/astarray.rb
+++ b/lib/puppet/parser/ast/astarray.rb
@@ -25,14 +25,10 @@ class Puppet::Parser::AST
# This is such a stupid hack. I've no real idea how to make a
# "real" declarative language, so I hack it so it looks like
# one, yay.
- definelist = [
- AST::CompDef, AST::NodeDef, AST::ClassDef
- ]
setlist = [
- AST::VarDef, AST::TypeDefaults, AST::Function
+ AST::VarDef, AST::ResourceDefaults, AST::Function
]
- definers = []
settors = []
others = []
@@ -53,15 +49,13 @@ class Puppet::Parser::AST
# Now sort them all according to the type of action
items.each { |child|
- if definelist.include?(child.class)
- definers << child
- elsif setlist.include?(child.class)
+ if setlist.include?(child.class)
settors << child
else
others << child
end
}
- rets = [definers, settors, others].flatten.collect { |child|
+ rets = [settors, others].flatten.collect { |child|
child.safeevaluate(:scope => scope)
}
else
@@ -107,37 +101,7 @@ class Puppet::Parser::AST
# Used for abstracting the grammar declarations. Basically unnecessary
# except that I kept finding bugs because I had too many arrays that
# meant completely different things.
- class ObjectInst < ASTArray; end
-
- # Another simple container class to make sure we can correctly arrayfy
- # things.
- class CompArgument < ASTArray
- @@warnings = {}
- def initialize(hash)
- super
- name = @children[0].value
-
- # If it's not a metaparamer, we're fine.
- return unless Puppet::Type.metaparamclass(name)
-
- if @children[1]
- if @children[1].value == false
- raise Puppet::ParseError,
- "%s is a metaparameter; please choose another name" %
- name
- else
- unless @@warnings[name]
- @@warnings[name] = true
- Puppet.warning "%s is a metaparam; this value will inherit to all contained elements" % name
- end
- end
- else
- raise Puppet::ParseError,
- "%s is a metaparameter; please choose another name" %
- name
- end
- end
- end
+ class ResourceInst < ASTArray; end
end
# $Id$
diff --git a/lib/puppet/parser/ast/classdef.rb b/lib/puppet/parser/ast/classdef.rb
deleted file mode 100644
index e09db985f..000000000
--- a/lib/puppet/parser/ast/classdef.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'puppet/parser/ast/compdef'
-
-class Puppet::Parser::AST
- # Define a new class. Syntactically similar to component definitions,
- # but classes are always singletons -- only one can exist on a given
- # host.
- class ClassDef < AST::CompDef
- @keyword = "class"
-
- def self.genclass
- AST::HostClass
- end
-
- def each
- if @parentclass
- #[@type,@args,@parentclass,@code].each { |child| yield child }
- [@type,@parentclass,@code].each { |child| yield child }
- else
- #[@type,@args,@code].each { |child| yield child }
- [@type,@code].each { |child| yield child }
- end
- end
-
- # Store our parse tree according to type.
- def disabled_evaluate(hash)
- scope = hash[:scope]
- type = @type.safeevaluate(:scope => scope)
- #args = @args.safeevaluate(:scope => scope)
-
- #:args => args,
- arghash = {
- :type => type,
- :code => @code
- }
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
- #Puppet.debug("defining hostclass '%s' with arguments [%s]" %
- # [type,args])
-
- begin
- hclass = HostClass.new(arghash)
- hclass.keyword = self.keyword
- scope.settype(type, hclass)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- end
-
- def tree(indent = 0)
- #@args.tree(indent + 1),
- return [
- @type.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("class")),
- @parentclass ? @parentclass.tree(indent + 1) : "",
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "class %s(%s) inherits %s {\n%s }" %
- [@type, @parentclass, @code]
- #[@type, @args, @parentclass, @code]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb
index 8e9acce57..fa480b179 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -1,91 +1,30 @@
-require 'puppet/rails'
+require 'puppet'
+require 'puppet/parser/ast/branch'
+require 'puppet/parser/collector'
+# An object that collects stored objects from the central cache and returns
+# them to the current host, yo.
class Puppet::Parser::AST
- # An object that collects stored objects from the central cache and returns
- # them to the current host, yo.
- class Collection < AST::Branch
- attr_accessor :type
+class Collection < AST::Branch
+ attr_accessor :type, :query, :form
- # We cannot evaluate directly here; instead we need to store a
- # CollectType object, which will do the collection. This is
- # the only way to find certain exported types in the current
- # configuration.
- def evaluate(hash)
- scope = hash[:scope]
+ # We return an object that does a late-binding evaluation.
+ def evaluate(hash)
+ scope = hash[:scope]
- @convertedtype = @type.safeevaluate(:scope => scope)
-
- scope.newcollection(self)
+ if self.query
+ q = self.query.safeevaluate :scope => scope
+ else
+ q = nil
end
- # Now perform the actual collection, yo.
- def perform(scope)
- # First get everything from the export table.
-
- # FIXME This will only find objects that are before us in the tree,
- # which is a problem.
- objects = scope.exported(@convertedtype)
-
- # We want to return all of these objects, and then whichever objects
- # we find in the db.
- array = objects.values
-
- # Mark all of these objects as collected, so that they also get
- # returned to the client. We don't store them in our scope
- # or anything, which is a little odd, but eh.
- array.each do |obj|
- obj.collected = true
- end
-
- count = array.length
-
- # Now we also have to see if there are any exported objects
- # in our own scope.
- scope.lookupexported(@convertedtype).each do |obj|
- objects[obj.name] = obj
- obj.collected = true
- end
-
- bucket = Puppet::TransBucket.new
-
- Puppet::Rails::RailsObject.find_all_by_collectable(true).each do |obj|
- # FIXME This should check that the source of the object is the
- # host we're running on, else it's a bad conflict.
- if objects.include?(obj.name)
- scope.debug("%s[%s] is already exported" % [@convertedtype, obj.name])
- next
- end
- count += 1
- trans = obj.to_trans
- bucket.push(trans)
+ newcoll = Puppet::Parser::Collector.new(scope, @type, q, self.form)
- args = {
- :name => trans.name,
- :type => trans.type,
- }
+ scope.newcollection(newcoll)
- [:file, :line].each do |param|
- if val = trans.send(param)
- args[param] = val
- end
- end
-
- args[:arguments] = {}
- trans.each do |p,v| args[:arguments][p] = v end
-
-
- # XXX Because the scopes don't expect objects to return values,
- # we have to manually add our objects to the scope. This is
- # uber-lame.
- scope.setobject(args)
- end
-
- scope.debug("Collected %s objects of type %s" %
- [count, @convertedtype])
-
- return bucket
- end
+ newcoll
end
end
+end
# $Id$
diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb
new file mode 100644
index 000000000..f69b2e92e
--- /dev/null
+++ b/lib/puppet/parser/ast/collexpr.rb
@@ -0,0 +1,81 @@
+require 'puppet'
+require 'puppet/parser/ast/branch'
+require 'puppet/parser/collector'
+
+# An object that collects stored objects from the central cache and returns
+# them to the current host, yo.
+class Puppet::Parser::AST
+class CollExpr < AST::Branch
+ attr_accessor :test1, :test2, :oper, :form, :type, :parens
+
+ # We return an object that does a late-binding evaluation.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # Make sure our contained expressions have all the info they need.
+ [@test1, @test2].each do |t|
+ if t.is_a?(self.class)
+ t.form ||= self.form
+ t.type ||= self.type
+ end
+ end
+
+ # The code is only used for virtual lookups
+ str1, code1 = @test1.safeevaluate :scope => scope
+ str2, code2 = @test2.safeevaluate :scope => scope
+
+ # First build up the virtual code.
+ # If we're a conjunction operator, then we're calling code. I did
+ # some speed comparisons, and it's at least twice as fast doing these
+ # case statements as doing an eval here.
+ code = proc do |resource|
+ case @oper
+ when "and": code1.call(resource) and code2.call(resource)
+ when "or": code1.call(resource) or code2.call(resource)
+ when "==": resource[str1] == str2
+ when "!=": resource[str1] != str2
+ end
+ end
+
+ # Now build up the rails conditions code
+ if self.parens and self.form == :exported
+ Puppet.warning "Parentheses are ignored in Rails searches"
+ end
+
+ case @oper
+ when "and", "or": oper = @oper.upcase
+ when "==": oper = "="
+ else
+ oper = @oper
+ end
+
+ if oper == "=" or oper == "!="
+ # Add the rails association info where necessary
+ if str1 == "title"
+ str = "title #{oper} '#{str2}'"
+ else
+ unless self.form == :virtual or str1 == "title"
+ parsefail "Collection from the database only supports " +
+ "title matching currently"
+ end
+ str = "rails_parameters.name = '#{str1}' and " +
+ "rails_parameters.value #{oper} '#{str2}'"
+ end
+ else
+ str = [str1, oper, str2].join(" ")
+ end
+
+ return str, code
+ end
+
+ def initialize(hash = {})
+ super
+
+ unless %w{== != and or}.include?(@oper)
+ raise ArgumentError, "Invalid operator %s" % @oper
+ end
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/compdef.rb b/lib/puppet/parser/ast/compdef.rb
deleted file mode 100644
index 17ee60181..000000000
--- a/lib/puppet/parser/ast/compdef.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-class Puppet::Parser::AST
- # Define a new component. This basically just stores the
- # associated parse tree by name in our current scope. Note that
- # there is currently a mismatch in how we look up components -- it
- # usually uses scopes, but sometimes uses '@@settypes'.
- # FIXME This class should verify that each of its direct children
- # has an abstractable name -- i.e., if a file does not include a
- # variable in its name, then the user is essentially guaranteed to
- # encounter an error if the component is instantiated more than
- # once.
- class CompDef < AST::Branch
- attr_accessor :type, :args, :code, :scope, :parentclass
- attr_writer :keyword
-
- @keyword = "define"
-
- class << self
- attr_reader :keyword
- end
-
-
- def self.genclass
- AST::Component
- end
-
- def each
- [@type,@args,@code].each { |child| yield child }
- end
-
- # Store the parse tree.
- def evaluate(hash)
- scope = hash[:scope]
- arghash = {:code => @code}
- arghash[:type] = @type.safeevaluate(:scope => scope)
-
- if @args
- arghash[:args] = @args.safeevaluate(:scope => scope)
- end
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
-
- begin
- comp = self.class.genclass.new(arghash)
- comp.keyword = self.keyword
- scope.settype(arghash[:type], comp)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- end
-
- def initialize(hash)
- @parentclass = nil
- @args = nil
- super
-
- #if @parentclass
- # Puppet.notice "Parent class of %s is %s" %
- # [@type.value, @parentclass.value]
- #end
-
- #Puppet.debug "Defining type %s" % @type.value
- end
-
- def keyword
- if defined? @keyword
- @keyword
- else
- self.class.keyword
- end
- end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("define")),
- @args.tree(indent + 1),
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "define %s(%s) {\n%s }" % [@type, @args, @code]
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
index ebbf21959..312b11d99 100644
--- a/lib/puppet/parser/ast/component.rb
+++ b/lib/puppet/parser/ast/component.rb
@@ -1,80 +1,68 @@
+require 'puppet/parser/ast/branch'
+
class Puppet::Parser::AST
# Evaluate the stored parse tree for a given component. This will
# receive the arguments passed to the component and also the type and
# name of the component.
class Component < AST::Branch
+ include Puppet::Util
+ include Puppet::Util::Warnings
+ include Puppet::Util::MethodHelper
class << self
attr_accessor :name
end
# The class name
- @name = :component
+ @name = :definition
- attr_accessor :type, :args, :code, :scope, :keyword
- attr_accessor :collectable, :parentclass
+ attr_accessor :type, :arguments, :code, :scope, :keyword
+ attr_accessor :exported, :namespace, :fqname, :interp
# These are retrieved when looking up the superclass
- attr_accessor :name, :arguments
+ attr_accessor :name
def evaluate(hash)
origscope = hash[:scope]
objtype = hash[:type]
- @name = hash[:name]
- @arguments = hash[:arguments] || {}
+ name = hash[:name]
+ args = symbolize_options(hash[:arguments] || {})
- @collectable = hash[:collectable]
+ exported = hash[:exported]
pscope = origscope
- #pscope = if ! Puppet[:lexical] or hash[:asparent] == false
- # origscope
- #else
- # @scope
- #end
- scope = pscope.newscope(
- :type => @type,
- :name => @name,
- :keyword => self.keyword
- )
- newcontext = hash[:newcontext]
+ scope = subscope(pscope, name)
- if @collectable or origscope.collectable
- scope.collectable = true
+ if exported or origscope.exported?
+ scope.exported = true
end
-
- unless self.is_a? AST::HostClass and ! newcontext
- #scope.warning "Setting context to %s" % self.object_id
- scope.context = self.object_id
- end
- @scope = scope
-
+ scope = scope
# Additionally, add a tag for whatever kind of class
# we are
- scope.tag(@type)
+ if @type != ""
+ scope.tag(@type)
+ end
- unless @name.nil?
- scope.tag(@name)
+ unless name.nil? or name =~ /[^\w]/
+ scope.tag(name)
end
# define all of the arguments in our local scope
- if self.args
+ if self.arguments
# Verify that all required arguments are either present or
# have been provided with defaults.
# FIXME This should probably also require each parent
# class's arguments...
- self.args.each { |arg, default|
- unless @arguments.include?(arg)
+ self.arguments.each { |arg, default|
+ arg = symbolize(arg)
+ unless args.include?(arg)
if defined? default and ! default.nil?
- @arguments[arg] = default
+ default = default.safeevaluate :scope => scope
+ args[arg] = default
#Puppet.debug "Got default %s for %s in %s" %
# [default.inspect, arg.inspect, @name.inspect]
else
- error = Puppet::ParseError.new(
- "Must pass %s to %s of type %s" %
- [arg.inspect,@name,@type]
- )
- error.line = self.line
- error.file = self.file
- raise error
+ parsefail "Must pass %s to %s of type %s" %
+ [arg,name,@type]
end
end
}
@@ -82,54 +70,116 @@ class Puppet::Parser::AST
# Set each of the provided arguments as variables in the
# component's scope.
- @arguments.each { |arg,value|
- begin
- scope.setvar(arg,@arguments[arg])
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => except
- error = Puppet::ParseError.new(except.message)
- error.line = self.line
- error.file = self.file
- error.set_backtrace except.backtrace
- raise error
+ args.each { |arg,value|
+ unless validattr?(arg)
+ parsefail "%s does not accept attribute %s" % [@type, arg]
+ end
+
+ exceptwrap do
+ scope.setvar(arg.to_s,args[arg])
end
}
- unless @arguments.include? "name"
- scope.setvar("name",@name)
+ unless args.include? "name"
+ scope.setvar("name",name)
end
- # Now just evaluate the code with our new bindings.
- #scope.inside(self) do # part of definition inheritance
- self.code.safeevaluate(:scope => scope)
- #end
+ if self.code
+ return self.code.safeevaluate(:scope => scope)
+ else
+ return nil
+ end
+ end
+
+ def initialize(hash = {})
+ @arguments = nil
+ @parentclass = nil
+ super
+
+ # Deal with metaparams in the argument list.
+ if @arguments
+ @arguments.each do |arg, defvalue|
+ next unless Puppet::Type.metaparamclass(arg)
+ if defvalue
+ warnonce "%s is a metaparam; this value will inherit to all contained elements" % arg
+ else
+ raise Puppet::ParseError,
+ "%s is a metaparameter; please choose another name" %
+ name
+ end
+ end
+ end
+ end
+
+ def parentclass
+ parentobj do |name|
+ @interp.findclass(namespace, name)
+ end
+ end
- # If we're being evaluated as a parent class, we want to return the
- # scope, so it can be overridden and such, but if not, we want to
- # return a TransBucket of our objects.
- if hash.include?(:asparent)
- return scope
+ # Set our parent class, with a little check to avoid some potential
+ # weirdness.
+ def parentclass=(name)
+ if name == @type
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentclass = name
+ end
+
+ # Hunt down our class object.
+ def parentobj
+ if @parentclass
+ # Cache our result, since it should never change.
+ unless @parentclass.is_a?(AST::HostClass)
+ unless tmp = yield(@parentclass)
+ parsefail "Could not find %s %s" % [self.class.name, @parentclass]
+ end
+
+ if tmp == self
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentclass = tmp
+ end
+ @parentclass
else
- return scope.to_trans
+ nil
end
end
+ # Create a new subscope in which to evaluate our code.
+ def subscope(scope, name = nil)
+ args = {
+ :type => @type,
+ :keyword => self.keyword,
+ :namespace => self.namespace
+ }
+
+ args[:name] = name if name
+ args[:type] = self.type if self.type
+ scope = scope.newscope(args)
+ scope.source = self
+
+ return scope
+ end
+
+ def to_s
+ fqname
+ end
+
# Check whether a given argument is valid. Searches up through
# any parent classes that might exist.
- def validarg?(param)
+ def validattr?(param)
+ return true if Puppet::Type.metaparam?(param)
+
+ param = param.to_s
found = false
- unless @args.is_a? Array
- @args = [@args]
+ unless @arguments.is_a? Array
+ @arguments = [@arguments]
end
- found = @args.detect { |arg|
+ found = @arguments.detect { |arg|
if arg.is_a? Array
arg[0] == param
else
diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb
index 2c63c8b76..f67531ae3 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -11,7 +11,10 @@ class Puppet::Parser::AST
args = @arguments.safeevaluate(:scope => scope)
- return scope.send("function_" + @name, args)
+ #exceptwrap :message => "Failed to execute %s" % @name,
+ # :type => Puppet::ParseError do
+ return scope.send("function_" + @name, args)
+ #end
end
def initialize(hash)
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index 44077983d..4a4664f0f 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -1,3 +1,5 @@
+require 'puppet/parser/ast/component'
+
class Puppet::Parser::AST
# The code associated with a class. This is different from components
# in that each class is a singleton -- only one will exist for a given
@@ -5,152 +7,50 @@ class Puppet::Parser::AST
class HostClass < AST::Component
@name = :class
+ # Are we a child of the passed class? Do a recursive search up our
+ # parentage tree to figure it out.
+ def child_of?(klass)
+ return false unless self.parentclass
+
+ if klass == self.parentclass
+ return true
+ else
+ return self.parentclass.child_of?(klass)
+ end
+ end
+
+ # Evaluate the code associated with this class.
def evaluate(hash)
scope = hash[:scope]
- objname = hash[:name]
args = hash[:arguments]
+
# Verify that we haven't already been evaluated
- # FIXME The second subclass won't evaluate the parent class
- # code at all, and any overrides will throw an error.
- if myscope = scope.lookupclass(self.object_id)
+ if scope.setclass?(self)
Puppet.debug "%s class already evaluated" % @type
-
- # Not used, but will eventually be used to fix #140.
- if myscope.is_a? Puppet::Parser::Scope
- unless scope.object_id == myscope.object_id
- #scope.parent = myscope
- end
- end
return nil
end
- # Set the class before we do anything else, so that it's set
- # during the evaluation and can be inspected.
- scope.setclass(self.object_id, @type)
-
- origscope = scope
-
- # Default to creating a new context
- newcontext = true
-
- # If we've got a parent, then we pass it the original scope we
- # received. It will get passed all the way up to the top class,
- # which will create a subscope and pass that subscope to its
- # subclass.
- if @parentscope = self.evalparent(
- :scope => scope, :arguments => args, :name => objname
- )
- if @parentscope.is_a? Puppet::TransBucket
- raise Puppet::DevError, "Got a bucket instead of a scope"
- end
-
- # Override our scope binding with the parent scope
- # binding.
- scope = @parentscope
-
- # But don't create a new context if our parent created one
- newcontext = false
- end
-
- # Just use the Component evaluate method, but change the type
- # to our own type.
- result = super(
- :scope => scope,
- :arguments => args,
- :type => @type,
- :name => objname, # might be nil
- :newcontext => newcontext,
- :asparent => hash[:asparent] || false # might be nil
- )
-
- # Now set the class again, this time using the scope. This way
- # we can look up the parent scope of this class later, so we
- # can hook the children together.
- scope.setscope(self.object_id, result)
-
- # This is important but painfully difficult. If we're the top-level
- # class, that is, we have no parent classes, then the transscope
- # is our own scope, but if there are parent classes, then the topmost
- # parent's scope is the transscope, since it contains its code and
- # all of the subclass's code.
- transscope ||= result
-
- if hash[:asparent]
- # If we're a parent class, then return the scope object itself.
- return result
- else
- transscope = nil
- if @parentscope
- transscope = @parentscope
- until transscope.parent.object_id == origscope.object_id
- transscope = transscope.parent
- end
+ if @parentclass
+ if pklass = self.parentclass
+ pklass.safeevaluate :scope => scope
else
- transscope = result
+ parsefail "Could not find class %s" % @parentclass
end
-
- # But if we're the final subclass, translate the whole scope tree
- # into TransObjects and TransBuckets.
- return transscope.to_trans
end
- end
- # Evaluate our parent class. Parent classes are evaluated in the
- # exact same scope as the children. This is maybe not a good idea
- # but, eh.
- #def evalparent(scope, args, name)
- def evalparent(hash)
- scope = hash[:scope]
- args = hash[:arguments]
- name = hash[:name]
- if @parentclass
- #scope.warning "parent class of %s is %s" %
- # [@type, @parentclass.inspect]
- parentobj = nil
+ # Set the class before we do anything else, so that it's set
+ # during the evaluation and can be inspected.
+ scope.setclass(self)
- begin
- parentobj = scope.lookuptype(@parentclass)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
- unless parentobj
- error = Puppet::ParseError.new(
- "Could not find parent '%s' of '%s'" %
- [@parentclass,@name])
- error.line = self.line
- error.file = self.file
- raise error
- end
+ unless hash[:nosubscope]
+ scope = subscope(scope)
+ end
- # Verify that the parent and child are of the same type
- unless parentobj.class == self.class
- error = Puppet::ParseError.new(
- "Class %s has incompatible parent type, %s vs %s" %
- [@type, parentobj.class, self.class]
- )
- error.file = self.file
- error.line = self.line
- raise error
- end
- # We don't need to pass the type, because the parent will just
- # use its own type. Specify that it's being evaluated as a parent,
- # so that it returns the scope, not a transbucket.
- return parentobj.safeevaluate(
- :scope => scope,
- :arguments => args,
- :name => name,
- :asparent => true,
- :collectable => self.collectable
- )
+ # Now evaluate our code, yo.
+ if self.code
+ return self.code.evaluate(:scope => scope)
else
- return false
+ return nil
end
end
@@ -158,7 +58,7 @@ class Puppet::Parser::AST
@parentclass = nil
super
end
-
end
-
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index 7b9a283d7..298c0168f 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -28,15 +28,11 @@ class Puppet::Parser::AST
def initialize(hash)
super
- unless @value == 'true' or @value == 'false'
+ unless @value == true or @value == false
raise Puppet::DevError,
"'%s' is not a boolean" % @value
end
- if @value == 'true'
- @value = true
- else
- @value = false
- end
+ @value
end
end
@@ -69,6 +65,9 @@ class Puppet::Parser::AST
# Lower-case words.
class Name < AST::Leaf; end
+ # double-colon separated class names
+ class ClassName < AST::Leaf; end
+
# Host names, either fully qualified or just the short name
class HostName < AST::Leaf
def initialize(hash)
@@ -87,18 +86,8 @@ class Puppet::Parser::AST
# Looks up the value of the object in the scope tree (does
# not include syntactical constructs, like '$' and '{}').
def evaluate(hash)
- begin
+ parsewrap do
return hash[:scope].lookupvar(@value)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::DevError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
end
end
end
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index e4e69bed9..3f508b2bf 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -1,113 +1,70 @@
+require 'puppet/parser/ast/hostclass'
+
class Puppet::Parser::AST
# The specific code associated with a host. Nodes are annoyingly unlike
# other objects. That's just the way it is, at least for now.
class Node < AST::HostClass
@name = :node
- attr_accessor :type, :args, :code, :parentclass
+ attr_accessor :name
#def evaluate(scope, facts = {})
def evaluate(hash)
- origscope = hash[:scope]
- facts = hash[:facts] || {}
-
- # nodes are never instantiated like a normal object,
- # but we need the type to be the name users would use for
- # instantiation, otherwise tags don't work out
+ scope = hash[:scope]
- pscope = origscope
#pscope = if ! Puppet[:lexical] or hash[:asparent]
# @scope
#else
# origscope
#end
- scope = pscope.newscope(
- :type => self.type,
- :keyword => @keyword
- )
- scope.context = self.object_id
+ Puppet.warning "%s => %s" % [scope.type, self.name]
- # Now set all of the facts inside this scope
- facts.each { |var, value|
- scope.setvar(var, value)
- }
-
- if tmp = self.evalparent(scope)
- # Again, override our scope with the parent scope, if
- # there is one.
- scope = tmp
+ # We don't have to worry about the declarativeness of node parentage,
+ # because the entry point is always a single node definition.
+ if parent = self.parentclass
+ scope = parent.safeevaluate :scope => scope
end
+ Puppet.notice "%s => %s" % [scope.type, self.name]
- #scope.tag(@name)
-
- # We never pass the facts to the parent class, because they've
- # already been defined at this top-level scope.
- #super(scope, facts, @name, @name)
+ scope = scope.newscope(
+ :type => self.name,
+ :keyword => @keyword,
+ :source => self,
+ :namespace => "" # nodes are always in ""
+ )
+ Puppet.info "%s => %s" % [scope.type, self.name]
# 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.setclass(self.object_id, @type.sub(/\..+/, ''))
+ scope.setclass(self)
- # And then evaluate our code.
- @code.safeevaluate(:scope => scope)
+ # And then evaluate our code if we have any
+ if self.code
+ @code.safeevaluate(:scope => scope)
+ end
return scope
end
- # Evaluate our parent class.
- def evalparent(scope)
- if @parentclass
- # This is pretty messed up. I don't know if this will
- # work in the long term, but we need to evaluate the node
- # in our own scope, even though our parent node has
- # a scope associated with it, because otherwise we 1) won't
- # get our facts defined, and 2) we won't actually get the
- # objects returned, based on how nodes work.
-
- # We also can't just evaluate the node itself, because
- # it would create a node scope within this scope,
- # and that would cause mass havoc.
- node = nil
-
- # The 'node' method just returns a hash of the node
- # code and name. It's used here, and in 'evalnode'.
- unless hash = scope.node(@parentclass)
- raise Puppet::ParseError,
- "Could not find parent node %s" %
- @parentclass
- end
-
- node = hash[:node]
- type = nil
- if type = node.type
- scope.tag(node.type)
- end
-
- begin
- code = node.code
- code.safeevaluate(:scope => scope, :asparent => true)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
+ def initialize(hash)
+ @parentclass = nil
+ super
- if node.parentclass
- node.evalparent(scope)
- end
+ # Do some validation on the node name
+ if @name =~ /[^-\w.]/
+ raise Puppet::ParseError, "Invalid node name %s" % @name
end
end
- def initialize(hash)
- @parentclass = nil
- super
+ def parentclass
+ parentobj do |name|
+ @interp.nodesearch(name)
+ end
+ @parentclass
end
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/nodedef.rb b/lib/puppet/parser/ast/nodedef.rb
deleted file mode 100644
index 06b104828..000000000
--- a/lib/puppet/parser/ast/nodedef.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-class Puppet::Parser::AST
- # Define a node. The node definition stores a parse tree for each
- # specified node, and this parse tree is only ever looked up when
- # a client connects.
- class NodeDef < AST::Branch
- attr_accessor :names, :code, :parentclass, :keyword, :scope
-
- def each
- [@names,@code].each { |child| yield child }
- end
-
- # Do implicit iteration over each of the names passed.
- def evaluate(hash)
- scope = hash[:scope]
- names = @names.safeevaluate(:scope => scope)
-
- unless names.is_a?(Array)
- names = [names]
- end
-
- names.each { |name|
- #Puppet.debug("defining host '%s' in scope %s" %
- # [name, scope.object_id])
- # We use 'type' here instead of name, because every component
- # type supports both 'type' and 'name', and 'type' is a more
- # appropriate description of the syntactic role that this term
- # plays.
- arghash = {
- :type => name,
- :code => @code
- }
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
- begin
- node = Node.new(arghash)
- node.keyword = true
- scope.setnode(name, node)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
- }
- end
-
- def initialize(hash)
- @parentclass = nil
- @keyword = "node"
- super
- end
-
- def tree(indent = 0)
- return [
- @names.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("node")),
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "node %s {\n%s }" % [@name, @code]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/objectdef.rb b/lib/puppet/parser/ast/objectdef.rb
deleted file mode 100644
index ac5fe3070..000000000
--- a/lib/puppet/parser/ast/objectdef.rb
+++ /dev/null
@@ -1,353 +0,0 @@
-class Puppet::Parser::AST
- # Any normal puppet object declaration. Can result in a class or a
- # component, in addition to builtin types.
- class ObjectDef < AST::Branch
- attr_accessor :name, :type, :collectable
- attr_reader :params
-
- # probably not used at all
- def []=(index,obj)
- @params[index] = obj
- end
-
- # probably not used at all
- def [](index)
- return @params[index]
- end
-
- # Iterate across all of our children.
- def each
- [@type,@name,@params].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Does not actually return an object; instead sets an object
- # in the current scope.
- def evaluate(hash)
- scope = hash[:scope]
- @scope = scope
- hash = {}
-
- # Get our type and name.
- objtype = @type.safeevaluate(:scope => scope)
-
- # Disable definition inheritance, for now. 8/27/06, luke
- #if objtype == "super"
- # objtype = supertype()
- # @subtype = true
- #else
- @subtype = false
- #end
-
- # If the type was a variable, we wouldn't have typechecked yet.
- # Do it now, if so.
- unless @checked
- self.typecheck(objtype)
- end
-
- # See if our object type was defined. If not, we know it's
- # builtin because we already typechecked.
- begin
- object = scope.lookuptype(objtype)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
-
- hash = {}
- # Evaluate all of the specified params.
- @params.each { |param|
- ary = param.safeevaluate(:scope => scope)
- hash[ary[0]] = ary[1]
- }
-
- # Now collect info from our parent.
- parentname = nil
- if @subtype
- parentname = supersetup(hash)
- end
-
- objnames = [nil]
- # Determine our name if we have one.
- if self.name
- objnames = @name.safeevaluate(:scope => scope)
- # it's easier to always use an array, even for only one name
- unless objnames.is_a?(Array)
- objnames = [objnames]
- end
- else
- if parentname
- objnames = [parentname]
- else
- # See if they specified the name as a parameter instead of
- # as a normal name (i.e., before the colon).
- unless object # we're a builtin
- if objclass = Puppet::Type.type(objtype)
- namevar = objclass.namevar
-
- tmp = hash["name"] || hash[namevar.to_s]
-
- if tmp
- objnames = [tmp]
- end
- else
- # this should never happen, because we've already
- # typechecked, but it's no real problem if it does
- # happen. We just end up with an object with no
- # name.
- end
- end
- end
- end
-
- # this is where our implicit iteration takes place;
- # if someone passed an array as the name, then we act
- # just like the called us many times
- objnames.collect { |objname|
- # If the object is a class, that means it's a builtin type, so
- # we just store it in the scope
- begin
- #Puppet.debug(
- # ("Setting object '%s' " +
- # "in scope %s " +
- # "with arguments %s") %
- # [objname, scope.object_id, hash.inspect]
- #)
- obj = scope.setobject(
- :type => objtype,
- :name => objname,
- :arguments => hash,
- :file => @file,
- :line => @line,
- :collectable => self.collectable
- )
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- }.reject { |obj| obj.nil? }
- end
-
- # Create our ObjectDef. Handles type checking for us.
- def initialize(hash)
- @checked = false
- super
-
- #self.typecheck(@type.value)
- end
-
- # Verify that all passed parameters are valid
- def paramcheck(builtin, objtype)
- # This defaults to true
- unless Puppet[:paramcheck]
- return
- end
-
- @params.each { |param|
- if builtin
- self.parambuiltincheck(builtin, param)
- else
- self.paramdefinedcheck(objtype, param)
- end
- }
-
- # Mark that we've made it all the way through.
- @checked = true
- end
-
- def parambuiltincheck(type, param)
- unless param.is_a?(AST::ObjectParam)
- raise Puppet::DevError,
- "Got something other than param"
- end
- begin
- pname = param.param.value
- rescue => detail
- raise Puppet::DevError, detail.to_s
- end
-
- return if pname == "name" # always allow these
- unless type.validattr?(pname)
- error = Puppet::ParseError.new(
- "Invalid parameter '%s' for type '%s'" %
- [pname, type.name]
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
-
- def paramdefinedcheck(objtype, param)
- # FIXME We might need to do more here eventually. Metaparams
- # behave strangely on containers.
- if Puppet::Type.metaparam?(param.param.value.intern)
- return
- end
-
- begin
- pname = param.param.value
- rescue => detail
- raise Puppet::DevError, detail.to_s
- end
-
- unless objtype.validarg?(pname)
- error = Puppet::ParseError.new(
- "Invalid parameter '%s' for type '%s'" %
- [pname,objtype.type]
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
-
- # Set the parameters for our object.
- def params=(params)
- if params.is_a?(AST::ASTArray)
- @params = params
- else
- @params = AST::ASTArray.new(
- :line => params.line,
- :file => params.file,
- :children => [params]
- )
- end
- end
-
- def supercomp
- unless defined? @supercomp
- if @scope and comp = @scope.inside
- @supercomp = comp
- else
- error = Puppet::ParseError.new(
- "'super' is only valid within definitions"
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supercomp
- end
-
- # Take all of the arguments of our parent and add them into our own,
- # without overriding anything.
- def supersetup(hash)
- comp = supercomp()
-
- # Now check each of the arguments from the parent.
- comp.arguments.each do |name, value|
- unless hash.has_key? name
- hash[name] = value
- end
- end
-
- # Return the parent name, so it can be used if appropriate.
- return comp.name
- end
-
- # Retrieve our supertype.
- def supertype
- unless defined? @supertype
- if parent = supercomp.parentclass
- @supertype = parent
- else
- error = Puppet::ParseError.new(
- "%s does not have a parent class" % comp.type
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supertype
- end
-
- # Print this object out.
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @name.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @params.collect { |param|
- begin
- param.tree(indent + 1)
- rescue NoMethodError => detail
- Puppet.err @params.inspect
- error = Puppet::DevError.new(
- "failed to tree a %s" % self.class
- )
- error.set_backtrace detail.backtrace
- raise error
- end
- }.join("\n")
- ].join("\n")
- end
-
- # Verify that the type is valid. This throws an error if there's
- # a problem, so the return value doesn't matter
- def typecheck(objtype)
- # This will basically always be on, but I wanted to make it at
- # least simple to turn off if it came to that
- unless Puppet[:typecheck]
- return
- end
-
- builtin = false
- begin
- builtin = Puppet::Type.type(objtype)
- rescue TypeError
- # nothing; we've already set builtin to false
- end
-
- typeobj = nil
- if builtin
- self.paramcheck(builtin, objtype)
- else
- # If there's no set scope, then we're in initialize, not
- # evaluate, so we can't test defined types.
- return true unless defined? @scope and @scope
-
- # Unless we can look up the type, throw an error
- unless typeobj = @scope.lookuptype(objtype)
- error = Puppet::ParseError.new(
- "Unknown type '%s'" % objtype
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
-
- # Now that we have the type, verify all of the parameters.
- # Note that we're now passing an AST Class object or whatever
- # as the type, not a simple string.
- self.paramcheck(builtin, typeobj)
- end
- end
-
- def to_s
- return "%s => { %s }" % [@name,
- @params.collect { |param|
- param.to_s
- }.join("\n")
- ]
- end
- end
-end
diff --git a/lib/puppet/parser/ast/objectref.rb b/lib/puppet/parser/ast/objectref.rb
deleted file mode 100644
index f9a63e222..000000000
--- a/lib/puppet/parser/ast/objectref.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-class Puppet::Parser::AST
- # A reference to an object. Only valid as an rvalue.
- class ObjectRef < AST::Branch
- attr_accessor :name, :type
-
- def each
- [@type,@name].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Evaluate our object, but just return a simple array of the type
- # and name.
- def evaluate(hash)
- scope = hash[:scope]
- objtype = @type.safeevaluate(:scope => scope)
- objnames = @name.safeevaluate(:scope => scope)
-
- # it's easier to always use an array, even for only one name
- unless objnames.is_a?(Array)
- objnames = [objnames]
- end
-
- # See if we can look the object up in our scope tree.
- begin
- object = scope.lookuptype(objtype)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
-
- # If the type isn't defined, verify that it's builtin
- unless object or Puppet::Type.type(objtype)
- error = Puppet::ParseError.new("Could not find type %s" %
- objtype.inspect)
- error.line = self.line
- error.file = self.file
- raise error
- end
-
- # should we implicitly iterate here?
- # yes, i believe that we essentially have to...
- ret = objnames.collect { |objname|
- if object.is_a?(AST::Component)
- objname = "%s[%s]" % [objtype,objname]
- objtype = "component"
- end
- [objtype,objname]
- }.reject { |obj| obj.nil? }
-
- # Return a flattened array, since we know that we've created an
- # array
- return *ret
- end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @name.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin))
- ].join("\n")
- end
-
- def to_s
- return "%s[%s]" % [@name,@type]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/resourcedef.rb b/lib/puppet/parser/ast/resourcedef.rb
new file mode 100644
index 000000000..1c9333030
--- /dev/null
+++ b/lib/puppet/parser/ast/resourcedef.rb
@@ -0,0 +1,215 @@
+require 'puppet/parser/ast/branch'
+
+# Any normal puppet object declaration. Can result in a class or a
+# component, in addition to builtin types.
+class Puppet::Parser::AST
+class ResourceDef < AST::Branch
+ attr_accessor :title, :type, :exported, :virtual
+ attr_reader :params
+
+ # probably not used at all
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ # probably not used at all
+ def [](index)
+ return @params[index]
+ end
+
+ # Iterate across all of our children.
+ def each
+ [@type,@title,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Does not actually return an object; instead sets an object
+ # in the current scope.
+ def evaluate(hash)
+ scope = hash[:scope]
+ @scope = scope
+ hash = {}
+
+ # Get our type and name.
+ objtype = @type
+
+ # Disable definition inheritance, for now. 8/27/06, luke
+ #if objtype == "super"
+ # objtype = supertype()
+ # @subtype = true
+ #else
+ @subtype = false
+ #end
+
+ # Evaluate all of the specified params.
+ paramobjects = @params.collect { |param|
+ param.safeevaluate(:scope => scope)
+ }
+
+ # Now collect info from our parent.
+ parentname = nil
+ if @subtype
+ parentname = supersetup(hash)
+ end
+
+ objtitles = nil
+ # Determine our name if we have one.
+ if self.title
+ objtitles = @title.safeevaluate(:scope => scope)
+ # it's easier to always use an array, even for only one name
+ unless objtitles.is_a?(Array)
+ objtitles = [objtitles]
+ end
+ else
+ if parentname
+ objtitles = [parentname]
+ else
+ # See if they specified the name as a parameter instead of
+ # as a normal name (i.e., before the colon).
+ unless object # we're a builtin
+ if objclass = Puppet::Type.type(objtype)
+ namevar = objclass.namevar
+
+ tmp = hash["name"] || hash[namevar.to_s]
+
+ if tmp
+ objtitles = [tmp]
+ end
+ else
+ # This isn't grammatically legal.
+ raise Puppet::ParseError, "Got a resource with no title"
+ end
+ end
+ end
+ end
+
+ # This is where our implicit iteration takes place; if someone
+ # passed an array as the name, then we act just like the called us
+ # many times.
+ objtitles.collect { |objtitle|
+ exceptwrap :type => Puppet::ParseError do
+ obj = Puppet::Parser::Resource.new(
+ :type => objtype,
+ :title => objtitle,
+ :params => paramobjects,
+ :file => @file,
+ :line => @line,
+ :exported => self.exported || scope.exported,
+ :virtual => self.virtual,
+ :source => scope.source,
+ :scope => scope
+ )
+
+ # And then store the resource in the scope.
+ # XXX At some point, we need to switch all of this to return
+ # objects instead of storing them like this.
+ scope.setresource(obj)
+ obj
+ end
+ }.reject { |obj| obj.nil? }
+ end
+
+ # Create our ResourceDef. Handles type checking for us.
+ def initialize(hash)
+ @checked = false
+ super
+
+ #self.typecheck(@type.value)
+ end
+
+ # Set the parameters for our object.
+ def params=(params)
+ if params.is_a?(AST::ASTArray)
+ @params = params
+ else
+ @params = AST::ASTArray.new(
+ :line => params.line,
+ :file => params.file,
+ :children => [params]
+ )
+ end
+ end
+
+ def supercomp
+ unless defined? @supercomp
+ if @scope and comp = @scope.inside
+ @supercomp = comp
+ else
+ error = Puppet::ParseError.new(
+ "'super' is only valid within definitions"
+ )
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ end
+ @supercomp
+ end
+
+ # Take all of the arguments of our parent and add them into our own,
+ # without overriding anything.
+ def supersetup(hash)
+ comp = supercomp()
+
+ # Now check each of the arguments from the parent.
+ comp.arguments.each do |name, value|
+ unless hash.has_key? name
+ hash[name] = value
+ end
+ end
+
+ # Return the parent name, so it can be used if appropriate.
+ return comp.name
+ end
+
+ # Retrieve our supertype.
+ def supertype
+ unless defined? @supertype
+ if parent = supercomp.parentclass
+ @supertype = parent
+ else
+ error = Puppet::ParseError.new(
+ "%s does not have a parent class" % comp.type
+ )
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ end
+ @supertype
+ end
+
+ # Print this object out.
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @title.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @params.collect { |param|
+ begin
+ param.tree(indent + 1)
+ rescue NoMethodError => detail
+ Puppet.err @params.inspect
+ error = Puppet::DevError.new(
+ "failed to tree a %s" % self.class
+ )
+ error.set_backtrace detail.backtrace
+ raise error
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@title,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/typedefaults.rb b/lib/puppet/parser/ast/resourcedefaults.rb
index 2e11a1b94..44db4d465 100644
--- a/lib/puppet/parser/ast/typedefaults.rb
+++ b/lib/puppet/parser/ast/resourcedefaults.rb
@@ -1,32 +1,22 @@
class Puppet::Parser::AST
- # A statement syntactically similar to an ObjectDef, but uses a
+ # A statement syntactically similar to an ResourceDef, but uses a
# capitalized object type and cannot have a name.
- class TypeDefaults < AST::Branch
+ class ResourceDefaults < AST::Branch
attr_accessor :type, :params
def each
[@type,@params].each { |child| yield child }
end
- # As opposed to ObjectDef, this stores each default for the given
+ # As opposed to ResourceDef, this stores each default for the given
# object type.
def evaluate(hash)
scope = hash[:scope]
- type = @type.safeevaluate(:scope => scope)
+ type = @type.downcase
params = @params.safeevaluate(:scope => scope)
- begin
- scope.setdefaults(type.downcase,params)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
+ parsewrap do
+ scope.setdefaults(type, params)
end
end
@@ -44,3 +34,5 @@ class Puppet::Parser::AST
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/resourceoverride.rb b/lib/puppet/parser/ast/resourceoverride.rb
new file mode 100644
index 000000000..26d69ae97
--- /dev/null
+++ b/lib/puppet/parser/ast/resourceoverride.rb
@@ -0,0 +1,62 @@
+require 'puppet/parser/ast/resourcedef'
+
+class Puppet::Parser::AST
+ # Set a parameter on a resource specification created somewhere else in the
+ # configuration. The object is responsible for verifying that this is allowed.
+ class ResourceOverride < ResourceDef
+ attr_accessor :object
+ attr_reader :params
+
+ # Iterate across all of our children.
+ def each
+ [@object,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Does not actually return an object; instead sets an object
+ # in the current scope.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # Get our object reference.
+ object = @object.safeevaluate(:scope => scope)
+
+ hash = {}
+
+ # Evaluate all of the specified params.
+ params = @params.collect { |param|
+ param.safeevaluate(:scope => scope)
+ }
+
+ # Now we just create a normal resource, but we call a very different
+ # method on the scope.
+ obj = Puppet::Parser::Resource.new(
+ :type => object.type,
+ :title => object.title,
+ :params => params,
+ :file => @file,
+ :line => @line,
+ :source => scope.source,
+ :scope => scope
+ )
+
+ # Now we tell the scope that it's an override, and it behaves as
+ # necessary.
+ scope.setoverride(obj)
+
+ obj
+ end
+
+ # Create our ResourceDef. Handles type checking for us.
+ def initialize(hash)
+ @checked = false
+ super
+
+ #self.typecheck(@type.value)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/objectparam.rb b/lib/puppet/parser/ast/resourceparam.rb
index 87c3e5e8e..248e91b32 100644
--- a/lib/puppet/parser/ast/objectparam.rb
+++ b/lib/puppet/parser/ast/resourceparam.rb
@@ -1,6 +1,8 @@
+require 'puppet/parser/ast/branch'
+
class Puppet::Parser::AST
- # The AST object for the parameters inside ObjectDefs and Selectors.
- class ObjectParam < AST::Branch
+ # The AST object for the parameters inside ResourceDefs and Selectors.
+ class ResourceParam < AST::Branch
attr_accessor :value, :param
def each
@@ -10,9 +12,17 @@ class Puppet::Parser::AST
# Return the parameter and the value.
def evaluate(hash)
scope = hash[:scope]
- param = @param.safeevaluate(:scope => scope)
+ param = @param
value = @value.safeevaluate(:scope => scope)
- return [param, value]
+
+ args = {:name => param, :value => value, :source => scope.source}
+ [:line, :file].each do |p|
+ if v = self.send(p)
+ args[p] = v
+ end
+ end
+
+ return Puppet::Parser::Resource::Param.new(args)
end
def tree(indent = 0)
@@ -27,5 +37,6 @@ class Puppet::Parser::AST
return "%s => %s" % [@param,@value]
end
end
-
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/resourceref.rb b/lib/puppet/parser/ast/resourceref.rb
new file mode 100644
index 000000000..b0fe5f6d7
--- /dev/null
+++ b/lib/puppet/parser/ast/resourceref.rb
@@ -0,0 +1,44 @@
+require 'puppet/parser/ast/branch'
+
+class Puppet::Parser::AST
+ # A reference to an object. Only valid as an rvalue.
+ class ResourceRef < AST::Branch
+ attr_accessor :title, :type
+
+ def each
+ [@type,@title].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Evaluate our object, but just return a simple array of the type
+ # and name.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # We want a lower-case type.
+ objtype = @type.downcase
+
+ title = @title.safeevaluate(:scope => scope)
+
+ return Puppet::Parser::Resource::Reference.new(
+ :type => objtype, :title => title
+ )
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @title.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin))
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s[%s]" % [@type,@title]
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb
index 5d1c41494..fe3fe9eaf 100644
--- a/lib/puppet/parser/ast/selector.rb
+++ b/lib/puppet/parser/ast/selector.rb
@@ -42,12 +42,8 @@ class Puppet::Parser::AST
if default
retvalue = default.value.safeevaluate(:scope => scope)
else
- error = Puppet::ParseError.new(
+ self.fail Puppet::ParseError,
"No value for selector param '%s'" % paramvalue
- )
- error.line = self.line
- error.file = self.file
- raise error
end
end
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index 79129f31a..30eef204a 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -10,18 +10,8 @@ class Puppet::Parser::AST
name = @name.safeevaluate(:scope => scope)
value = @value.safeevaluate(:scope => scope)
- begin
+ parsewrap do
scope.setvar(name,value)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
end
end
@@ -43,3 +33,5 @@ class Puppet::Parser::AST
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb
new file mode 100644
index 000000000..f3c7949de
--- /dev/null
+++ b/lib/puppet/parser/collector.rb
@@ -0,0 +1,97 @@
+# An object that collects stored objects from the central cache and returns
+# them to the current host, yo.
+class Puppet::Parser::Collector
+ attr_accessor :type, :scope, :query, :form
+
+ # Collect exported objects.
+ def collect_exported
+ require 'puppet/rails'
+ # First get everything from the export table. Just reuse our
+ # collect_virtual method but tell it to use 'exported? for the test.
+ resources = collect_virtual(true)
+
+ count = resources.length
+
+ # We're going to collect objects from rails, but we don't want any
+ # objects from this host.
+ host = Puppet::Rails::Host.find_by_name(@scope.host)
+
+ args = {}
+ if host
+ args[:conditions] = "host_id != #{host.id}"
+ else
+ #Puppet.info "Host %s is uninitialized" % @scope.host
+ end
+
+ # Now look them up in the rails db. When we support attribute comparison
+ # and such, we'll need to vary the conditions, but this works with no
+ # attributes, anyway.
+ Puppet::Util.benchmark(:debug, "Collected #{self.type} resources") do
+ Puppet::Rails::RailsResource.find_all_by_restype_and_exported(@type, true,
+ args
+ ).each do |obj|
+ count += 1
+ resource = obj.to_resource(self.scope)
+
+ # XXX Because the scopes don't expect objects to return values,
+ # we have to manually add our objects to the scope. This is
+ # uber-lame.
+ scope.setresource(resource)
+
+ resources << resource
+ end
+ end
+
+ scope.debug("Collected %s objects of type %s" %
+ [count, @convertedtype])
+
+ return resources
+ end
+
+ # Collect just virtual objects, from our local configuration.
+ def collect_virtual(exported = false)
+ if exported
+ method = :exported?
+ else
+ method = :virtual?
+ end
+ scope.resources.find_all do |resource|
+ resource.type == @type and resource.send(method) and match?(resource)
+ end
+ end
+
+ # Call the collection method, mark all of the returned objects as non-virtual,
+ # and then delete this object from the list of collections to evaluate.
+ def evaluate
+ method = "collect_#{@form.to_s}"
+ objects = send(method).each do |obj|
+ obj.virtual = false
+ end
+
+ # And then remove us from the list of collections, since we've
+ # now been evaluated.
+ @scope.collections.delete(self)
+
+ objects
+ end
+
+ def initialize(scope, type, query, form)
+ @scope = scope
+ @type = type
+ @query = query
+ @form = form
+ @tests = []
+ end
+
+ # Does the resource match our tests? We don't yet support tests,
+ # so it's always true at the moment.
+ def match?(resource)
+ if self.query
+ return self.query.call(resource)
+ else
+ return true
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 9562441bc..96fccd7cd 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -76,29 +76,24 @@ module Functions
# Include the specified classes
newfunction(:include) do |vals|
- vals.each do |val|
- if objecttype = lookuptype(val)
- # It's a defined type, so set it into the scope so it can
- # be evaluated.
- setobject(
- :type => val,
- :arguments => {}
- )
- else
- raise Puppet::ParseError, "Unknown class %s" % val
- end
+ klasses = evalclasses(*vals)
+
+ missing = vals.find_all do |klass|
+ ! klass.include?(klass)
+ end
+
+ # Throw an error if we didn't evaluate all of the classes.
+ if missing.length == 1
+ self.fail Puppet::ParseError,
+ "Could not find class %s" % missing
+ elsif missing.length > 1
+ self.fail Puppet::ParseError,
+ "Could not find classes %s" % missing.join(", ")
end
end
# Tag the current scope with each passed name
newfunction(:tag) do |vals|
- vals.each do |val|
- # Some hackery, because the tags are stored by object id
- # for singletonness.
- self.setclass(val.object_id, val)
- end
-
- # Also add them as tags
self.tag(*vals)
end
@@ -120,16 +115,13 @@ module Functions
# Test whether a given class or definition is defined
newfunction(:defined, :rvalue) do |vals|
- retval = true
-
- vals.each do |val|
- unless builtintype?(val) or lookuptype(val)
- retval = false
- break
- end
+ # For some reason, it doesn't want me to return from here.
+ if vals.detect do |val| Puppet::Type.type(val) or finddefine(val) end
+ true
+ else
+ false
end
- return retval
end
newfunction(:fail, :statement) do |vals|
@@ -151,7 +143,7 @@ module Functions
# Use a wrapper, so the template can't get access to the full
# Scope object.
debug "Retrieving template %s" % file
- wrapper = Puppet::Parser::Scope::TemplateWrapper.new(self, file)
+ wrapper = Puppet::Parser::TemplateWrapper.new(self, file)
begin
wrapper.result()
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index 3a1a78e50..5c91aabdc 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -5,38 +5,52 @@
class Puppet::Parser::Parser
token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE
-token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON TYPE
+token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON TYPE 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
+token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSNAME CLASSREF
+token NOT OR AND
# We have 2 shift/reduce conflicts
#expect 2
rule
program: statements {
- # Make sure we always return an array.
- if val[0].is_a?(AST::ASTArray)
- result = val[0]
+ 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 = aryfy(val[0])
+ result = nil
end
}
- | nothing
+ | nil
-statements: statement
+statements: statement
| statements statement {
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[1])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[1]]
+ 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: object
- | collectable
+statement: resource
+ | virtualresource
| collection
| assignment
| casestatement
@@ -46,15 +60,16 @@ statement: object
| definition
| hostclass
| nodedef
+ | resourceoverride
-fstatement: NAME LPAREN classnames RPAREN {
+fstatement: NAME LPAREN namestrings RPAREN {
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
:arguments => args,
:ftype => :statement
}
- | NAME classnames {
+ | NAME namestrings {
args = aryfy(val[1])
result = ast AST::Function,
:name => val[0],
@@ -62,108 +77,163 @@ fstatement: NAME LPAREN classnames RPAREN {
:ftype => :statement
}
-# Includes are just syntactic sugar for classes with no names and
-# no arguments.
-#include: INCLUDE classnames {
-# result = function_include(val[1])
-#}
-
-# Define a new tag. Both of these functions should really be done generically,
-# but I'm not in a position to do that just yet. :/
-#tag: TAG classnames {
-# result = function_tag(val[1])
-#}
-
-classnames: classname
- | classnames COMMA classname {
+namestrings: namestring
+ | namestrings COMMA namestring {
result = aryfy(val[0], val[2])
result.line = @lexer.line
result.file = @lexer.file
}
-classname: name
+namestring: name
| variable
| quotedtext
-#object: name LBRACE objectname COLON params endcomma RBRACE {
-object: name LBRACE objectinstances endsemi RBRACE {
+resource: NAME LBRACE resourceinstances endsemi RBRACE {
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid name"
+ error "Invalid name"
end
array = val[2]
- if array.instance_of?(AST::ObjectInst)
+ if array.instance_of?(AST::ResourceInst)
array = [array]
end
result = ast AST::ASTArray
- # this iterates across each specified objectinstance
+ # this iterates across each specified resourceinstance
array.each { |instance|
- unless instance.instance_of?(AST::ObjectInst)
+ unless instance.instance_of?(AST::ResourceInst)
raise Puppet::Dev, "Got something that isn't an instance"
end
# now, i need to somehow differentiate between those things with
# arrays in their names, and normal things
- result.push ast(AST::ObjectDef,
+ result.push ast(AST::ResourceDef,
:type => val[0],
- :name => instance[0],
+ :title => instance[0],
:params => instance[1])
}
-} | name LBRACE params endcomma RBRACE {
- if val[0].instance_of?(AST::ASTArray)
- Puppet.notice "invalid name"
- raise Puppet::ParseError, "Invalid name"
- end
- # an object but without a name
- # this cannot be an instance of a library type
- result = ast AST::ObjectDef, :type => val[0], :params => val[2]
-
-} | type LBRACE params endcomma RBRACE {
+} | NAME LBRACE params endcomma RBRACE {
+ # This is a deprecated syntax.
+ error "All resource specifications require names"
+} | TYPE LBRACE params endcomma RBRACE {
# a template setting for a type
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid type"
+ error "Invalid type"
end
- result = ast(AST::TypeDefaults, :type => val[0], :params => val[2])
+ result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2])
+}
+
+# Override a value set elsewhere in the configuration.
+resourceoverride: resourceref LBRACE params RBRACE {
+ result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
}
-# Collectable objects; these get stored in the database, instead of
-# being passed to the client.
-collectable: AT object {
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+# 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]
+ error "You cannot collect without storeconfigs being set"
end
- if val[1].is_a? AST::TypeDefaults
- raise Puppet::ParseError, "Defaults are not collectable"
+ if val[1].is_a? AST::ResourceDefaults
+ error "Defaults are not virtualizable"
end
- # Just mark our objects as collectable and pass them through.
+ 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.collectable = true
+ obj.send(method, true)
end
else
- val[1].collectable = true
+ 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: name LCOLLECT RCOLLECT {
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+collection: collectname 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]
+ error "You cannot collect exported resources without storeconfigs being set"
end
- result = ast AST::Collection, :type => val[0]
+ result = ast AST::Collection, args
}
-objectinst: objectname COLON params endcomma {
- result = ast AST::ObjectInst, :children => [val[0],val[2]]
+collectname: TYPE | NAME
+
+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 | OR
-objectinstances: objectinst
- | objectinstances SEMIC objectinst {
- if val[0].instance_of?(AST::ObjectInst)
+collexpr: colllval ISEQUAL simplervalue {
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+}
+ | colllval NOTEQUAL simplervalue {
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+}
+
+colllval: variable
+ | name
+
+resourceinst: resourcename COLON params endcomma {
+ result = ast AST::ResourceInst, :children => [val[0],val[2]]
+}
+
+resourceinstances: resourceinst
+ | resourceinstances SEMIC resourceinst {
+ if val[0].instance_of?(AST::ResourceInst)
result = ast AST::ASTArray, :children => [val[0],val[2]]
else
val[0].push val[2]
@@ -182,7 +252,7 @@ type: TYPE {
result = ast AST::Type, :value => val[0]
}
-objectname: quotedtext
+resourcename: quotedtext
| name
| type
| selector
@@ -191,7 +261,7 @@ objectname: quotedtext
assignment: VARIABLE EQUALS rvalue {
# this is distinct from referencing a variable
- variable = ast AST::Name, :value => val[0].sub(/^\$/,'')
+ variable = ast AST::Name, :value => val[0]
result = ast AST::VarDef, :name => variable, :value => val[2]
}
@@ -210,8 +280,7 @@ params: # nothing
}
param: NAME FARROW rvalue {
- leaf = ast AST::String, :value => val[0]
- result = ast AST::ObjectParam, :param => leaf, :value => val[2]
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
}
rvalues: rvalue
@@ -223,6 +292,13 @@ rvalues: rvalue
end
}
+simplervalue: quotedtext
+ | name
+ | type
+ | boolean
+ | selector
+ | variable
+
rvalue: quotedtext
| name
| type
@@ -230,11 +306,11 @@ rvalue: quotedtext
| selector
| variable
| array
- | objectref
+ | resourceref
| funcrvalue
# We currently require arguments in these functions.
-funcrvalue: NAME LPAREN classnames RPAREN {
+funcrvalue: NAME LPAREN namestrings RPAREN {
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
@@ -252,8 +328,11 @@ boolean: BOOLEAN {
result = ast AST::Boolean, :value => val[0]
}
-objectref: name LBRACK rvalue RBRACK {
- result = ast AST::ObjectRef, :type => val[0], :name => val[2]
+resourceref: NAME LBRACK rvalue RBRACK {
+ Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+} | TYPE LBRACK rvalue RBRACK {
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
}
ifstatement: IF iftest LBRACE statements RBRACE else {
@@ -333,7 +412,7 @@ sintvalues: selectval
}
selectval: selectlhand FARROW rvalue {
- result = ast AST::ObjectParam, :param => val[0], :value => val[2]
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
}
selectlhand: name
@@ -378,7 +457,7 @@ import: IMPORT quotedtext {
end
files.each { |file|
- parser = Puppet::Parser::Parser.new()
+ parser = Puppet::Parser::Parser.new(interp)
parser.files = self.files
Puppet.debug("importing '%s'" % file)
@@ -396,8 +475,13 @@ import: IMPORT quotedtext {
end
# push the results into the main result array
# We always return an array when we parse.
- parser.parse.each do |child|
- result.push child
+ ast = parser.parse
+
+ # Things that just get added to the classtable or whatever return nil
+ if ast
+ ast.each do |child|
+ result.push child
+ end
end
}
}
@@ -405,165 +489,99 @@ import: IMPORT quotedtext {
# Disable definition inheritance for now. 8/27/06, luke
#definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE {
-definition: DEFINE NAME argumentlist LBRACE statements RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => val[4] # Switch to 5 for parents
- }
+definition: DEFINE fqname argumentlist LBRACE statements RBRACE {
+ interp.newdefine fqname(val[1]), :arguments => val[2], :code => val[4]
+ @lexer.indefine = false
+ result = nil
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
- result = ast AST::CompDef, args
#} | DEFINE NAME argumentlist parent LBRACE RBRACE {
-} | DEFINE NAME argumentlist LBRACE RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => ast(AST::ASTArray)
- }
-
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
-
- result = ast AST::CompDef, args
+} | DEFINE fqname argumentlist LBRACE RBRACE {
+ interp.newdefine fqname(val[1]), :arguments => val[2]
+ @lexer.indefine = false
+ result = nil
}
#hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
-hostclass: CLASS NAME parent LBRACE statements RBRACE {
- #:args => val[2],
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => val[4]
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
-} | CLASS NAME parent LBRACE RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => ast(AST::ASTArray, :children => [])
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
+hostclass: CLASS fqname parent LBRACE statements RBRACE {
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :code => val[4], :parent => val[2]
+ result = nil
+} | CLASS fqname parent LBRACE RBRACE {
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :parent => val[2]
+ result = nil
}
nodedef: NODE hostnames parent LBRACE statements RBRACE {
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => val[4]
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef, args
+ interp.newnode val[1], :parent => val[2], :code => val[4]
+ result = nil
} | NODE hostnames parent LBRACE RBRACE {
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => ast(AST::ASTArray, :children => [])
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef,args
+ interp.newnode val[1], :parent => val[2]
+ result = nil
}
-# Multiple hostnames, as used for node names.
+fqname: NAME
+ | CLASSNAME
+
+# Multiple hostnames, as used for node names. These are all literal
+# strings, not AST objects.
hostnames: hostname
| hostnames COMMA hostname {
- if val[0].instance_of?(AST::ASTArray)
- result = val[0]
- result.push val[2]
- else
- result = ast AST::ASTArray, :children => [val[0], val[2]]
- end
+ result = val[0]
+ result = [result] unless result.is_a?(Array)
+ result << val[2]
}
-hostname: NAME {
- result = ast AST::HostName, :value => val[0]
-} | SQTEXT {
- result = ast AST::HostName, :value => val[0]
-} | DEFAULT {
- result = ast AST::Default, :value => val[0]
+hostname: NAME
+ | SQTEXT
+ | DEFAULT
+
+nil: {
+ result = nil
}
nothing: {
result = ast AST::ASTArray, :children => []
}
-argumentlist: nothing
+argumentlist: nil
| LPAREN nothing RPAREN {
- result = val[1]
+ result = nil
}
| LPAREN arguments RPAREN {
- if val[1].instance_of?(AST::ASTArray)
- result = val[1]
- else
- result = ast AST::ASTArray, :children => [val[1]]
- end
+ result = val[1]
+ result = [result] unless result[0].is_a?(Array)
}
arguments: argument
| arguments COMMA argument {
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[2])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[2]]
- end
+ result = val[0]
+ result = [result] unless result[0].is_a?(Array)
+ result << val[2]
}
-argument: name EQUALS rvalue {
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0],val[2]]
+argument: NAME EQUALS rvalue {
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0], val[2]]
}
- | name {
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0]]
-} | lvariable EQUALS rvalue {
- result = ast AST::CompArgument, :children => [val[0],val[2]]
-} | lvariable {
- result = ast AST::CompArgument, :children => [val[0]]
+ | NAME {
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0]]
+} | VARIABLE EQUALS rvalue {
+ result = [val[0], val[2]]
+} | VARIABLE {
+ result = [val[0]]
}
-parent: nothing
+parent: nil
| INHERITS NAME {
- result = ast AST::Name, :value => val[1]
+ result = val[1]
}
variable: VARIABLE {
- name = val[0].sub(/^\$/,'')
- result = ast AST::Variable, :value => name
-}
-
-# This is variables as lvalues; we're assigning them, not deferencing them.
-lvariable: VARIABLE {
- result = ast AST::Name, :value => val[0].sub(/^\$/,'')
+ result = ast AST::Variable, :value => val[0]
}
array: LBRACK rvalues RBRACK {
@@ -602,9 +620,21 @@ Puppet[:paramcheck] = true
---- inner ----
require 'puppet/parser/functions'
-attr_reader :file
+attr_reader :file, :interp
attr_accessor :files
+# Add context to a message; useful for error messages and such.
+def addcontext(message, obj = nil)
+ obj ||= @lexer
+
+ message += " on line %s" % obj.line
+ if file = obj.file
+ message += " in file %s" % file
+ end
+
+ return message
+end
+
# Create an AST array out of all of the args
def aryfy(*args)
if args[0].instance_of?(AST::ASTArray)
@@ -636,6 +666,17 @@ def ast(klass, hash = nil)
return klass.new(hash)
end
+# Raise a Parse error.
+def error(message)
+ except = Puppet::ParseError.new(message)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
def file=(file)
unless FileTest.exists?(file)
unless file =~ /\.pp$/
@@ -653,23 +694,20 @@ def file=(file)
end
end
-def initialize
+def initialize(interpreter)
+ @interp = interpreter
+ initvars()
+end
+
+# Initialize or reset all of our variables.
+def initvars
@lexer = Puppet::Parser::Lexer.new()
@files = []
- #if Puppet[:debug]
- # @yydebug = true
- #end
end
-# Add a new file to be checked when we're checking to see if we should be
-# reparsed.
-def newfile(*files)
- files.each do |file|
- unless file.is_a? Puppet::LoadedFile
- file = Puppet::LoadedFile.new(file)
- end
- @files << file
- end
+# The fully qualifed name, with the full namespace.
+def fqname(name)
+ [@lexer.namespace, name].join("::").sub(/^::/, '')
end
def on_error(token,value,stack)
@@ -693,7 +731,7 @@ def parse(string = nil)
self.string = string
end
begin
- yyparse(@lexer,:scan)
+ main = yyparse(@lexer,:scan)
rescue Racc::ParseError => except
error = Puppet::ParseError.new(except)
error.line = @lexer.line
@@ -720,6 +758,13 @@ def parse(string = nil)
error.set_backtrace except.backtrace
raise error
end
+ if main
+ # Store the results as the top-level class.
+ interp.newclass("", :code => main)
+ return main
+ end
+ensure
+ @lexer.clear
end
# See if any of the files have changed.
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 26bf8104e..111fe2ed3 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -3,463 +3,774 @@
# and calls out to other objects.
require 'puppet'
+require 'timeout'
require 'puppet/parser/parser'
require 'puppet/parser/scope'
+class Puppet::Parser::Interpreter
+ include Puppet::Util
+
+ Puppet.setdefaults("ldap",
+ :ldapnodes => [false,
+ "Whether to search for node configurations in LDAP."],
+ :ldapssl => [false,
+ "Whether SSL should be used when searching for nodes.
+ Defaults to false because SSL usually requires certificates
+ to be set up on the client side."],
+ :ldaptls => [false,
+ "Whether TLS should be used when searching for nodes.
+ Defaults to false because TLS usually requires certificates
+ to be set up on the client side."],
+ :ldapserver => ["ldap",
+ "The LDAP server. Only used if ``ldapnodes`` is enabled."],
+ :ldapport => [389,
+ "The LDAP port. Only used if ``ldapnodes`` is enabled."],
+ :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))",
+ "The search string used to find an LDAP node."],
+ :ldapattrs => ["puppetclass",
+ "The LDAP attributes to use to define Puppet classes. Values
+ should be comma-separated."],
+ :ldapparentattr => ["parentnode",
+ "The attribute to use to define the parent node."],
+ :ldapuser => ["",
+ "The user to use to connect to LDAP. Must be specified as a
+ full DN."],
+ :ldappassword => ["",
+ "The password to use to connect to LDAP."],
+ :ldapbase => ["",
+ "The search base for LDAP searches. It's impossible to provide
+ a meaningful default here, although the LDAP libraries might
+ have one already set. Generally, it should be the 'ou=Hosts'
+ branch under your main directory."]
+ )
+
+ Puppet.setdefaults(:puppetmaster,
+ :storeconfigs => [false,
+ "Whether to store each client's configuration. This
+ requires ActiveRecord from Ruby on Rails."]
+ )
+
+ attr_accessor :usenodes
+
+ class << self
+ attr_writer :ldap
+ end
+ # just shorten the constant path a bit, using what amounts to an alias
+ AST = Puppet::Parser::AST
+
+ include Puppet::Util::Errors
+
+ # Create an ldap connection. This is a class method so others can call
+ # it and use the same variables and such.
+ def self.ldap
+ unless defined? @ldap and @ldap
+ if Puppet[:ldapssl]
+ @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ elsif Puppet[:ldaptls]
+ @ldap = LDAP::SSLConn.new(
+ Puppet[:ldapserver], Puppet[:ldapport], true
+ )
+ else
+ @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ end
+ @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ end
-module Puppet
- module Parser
- class Interpreter
- include Puppet::Util
-
- Puppet.setdefaults("ldap",
- :ldapnodes => [false,
- "Whether to search for node configurations in LDAP."],
- :ldapssl => [false,
- "Whether SSL should be used when searching for nodes.
- Defaults to false because SSL usually requires certificates
- to be set up on the client side."],
- :ldaptls => [false,
- "Whether TLS should be used when searching for nodes.
- Defaults to false because TLS usually requires certificates
- to be set up on the client side."],
- :ldapserver => ["ldap",
- "The LDAP server. Only used if ``ldapnodes`` is enabled."],
- :ldapport => [389,
- "The LDAP port. Only used if ``ldapnodes`` is enabled."],
- :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))",
- "The search string used to find an LDAP node."],
- :ldapattrs => ["puppetclass",
- "The LDAP attributes to use to define Puppet classes. Values
- should be comma-separated."],
- :ldapparentattr => ["parentnode",
- "The attribute to use to define the parent node."],
- :ldapuser => ["",
- "The user to use to connect to LDAP. Must be specified as a
- full DN."],
- :ldappassword => ["",
- "The password to use to connect to LDAP."],
- :ldapbase => ["",
- "The search base for LDAP searches. It's impossible to provide
- a meaningful default here, although the LDAP libraries might
- have one already set. Generally, it should be the 'ou=Hosts'
- branch under your main directory."]
- )
-
- Puppet.setdefaults(:puppetmaster,
- :storeconfigs => [false,
- "Whether to store each client's configuration. This
- requires ActiveRecord from Ruby on Rails."]
- )
+ return @ldap
+ end
- attr_accessor :ast
+ def clear
+ initparsevars
+ end
- class << self
- attr_writer :ldap
- end
- # just shorten the constant path a bit, using what amounts to an alias
- AST = Puppet::Parser::AST
-
- # Create an ldap connection. This is a class method so others can call
- # it and use the same variables and such.
- def self.ldap
- unless defined? @ldap and @ldap
- if Puppet[:ldapssl]
- @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
- elsif Puppet[:ldaptls]
- @ldap = LDAP::SSLConn.new(
- Puppet[:ldapserver], Puppet[:ldapport], true
- )
- else
- @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ # Iteratively evaluate all of the objects. This finds all fo the
+ # objects that represent definitions and evaluates the definitions appropriately.
+ # It also adds defaults and overrides as appropriate.
+ def evaliterate(scope)
+ count = 0
+ begin
+ timeout 300 do
+ while ary = scope.unevaluated
+ ary.each do |resource|
+ resource.evaluate
end
- @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
- @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
- @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
end
-
- return @ldap
end
+ rescue Timeout::Error
+ raise Puppet::DevError, "Got a timeout trying to evaluate all definitions"
+ end
+ end
- # create our interpreter
- def initialize(hash)
- if @code = hash[:Code]
- @file = nil # to avoid warnings
- elsif ! @file = hash[:Manifest]
- raise Puppet::DevError, "You must provide code or a manifest"
- end
+ # Evaluate a specific node.
+ def evalnode(client, scope, facts)
+ return unless self.usenodes
- if hash.include?(:UseNodes)
- @usenodes = hash[:UseNodes]
- else
- @usenodes = true
- end
+ unless client
+ raise Puppet::Error,
+ "Cannot evaluate nodes with a nil client"
+ end
+ names = [client]
+
+ # Make sure both the fqdn and the short name of the
+ # host can be used in the manifest
+ if client =~ /\./
+ names << client.sub(/\..+/,'')
+ else
+ names << "#{client}.#{facts['domain']}"
+ end
- # By default, we only search the parse tree.
- @nodesources = []
+ if names.empty?
+ raise Puppet::Error,
+ "Cannot evaluate nodes with a nil client"
+ end
- if Puppet[:ldapnodes]
- @nodesources << :ldap
- end
+ # Look up our node object.
+ if nodeclass = nodesearch(*names)
+ nodeclass.safeevaluate :scope => scope
+ else
+ raise Puppet::Error, "Could not find %s with names %s" %
+ [client, names.join(", ")]
+ end
+ end
- if hash[:NodeSources]
- hash[:NodeSources].each do |src|
- if respond_to? "nodesearch_#{src.to_s}"
- @nodesources << src.to_s.intern
- else
- Puppet.warning "Node source '#{src}' not supported"
- end
- end
- end
+ # Evaluate all of the code we can find that's related to our client.
+ def evaluate(client, facts)
- @setup = false
+ scope = Puppet::Parser::Scope.new(:interp => self) # no parent scope
+ scope.name = "top"
+ scope.type = "main"
- # Set it to either the value or nil. This is currently only used
- # by the cfengine module.
- @classes = hash[:Classes] || []
+ scope.host = facts["hostname"] || Facter.value("hostname")
- @local = hash[:Local] || false
+ classes = @classes.dup
- if hash.include?(:ForkSave)
- @forksave = hash[:ForkSave]
- else
- # This is just too dangerous right now. Sorry, it's going
- # to have to be slow.
- @forksave = false
- end
+ # Okay, first things first. Set our facts.
+ scope.setfacts(facts)
- if Puppet[:storeconfigs]
- Puppet::Rails.init
- end
+ # Everyone will always evaluate the top-level class, if there is one.
+ if klass = findclass("", "")
+ # Set the source, so objects can tell where they were defined.
+ scope.source = klass
+ klass.safeevaluate :scope => scope, :nosubscope => true
+ end
- @files = []
+ # Next evaluate the node
+ evalnode(client, scope, facts)
- # Create our parser object
- parsefiles
+ # If we were passed any classes, evaluate those.
+ if classes
+ classes.each do |klass|
+ if klassobj = findclass("", klass)
+ klassobj.safeevaluate :scope => scope
+ end
end
+ end
- # Search for our node in the various locations. This only searches
- # locations external to the files; the scope is responsible for
- # searching the parse tree.
- def nodesearch(*nodes)
- # At this point, stop at the first source that defines
- # the node
- @nodesources.each do |source|
- method = "nodesearch_%s" % source
- parent = nil
- nodeclasses = nil
- if self.respond_to? method
- nodes.each do |node|
- parent, nodeclasses = self.send(method, node)
-
- if parent or (nodeclasses and !nodeclasses.empty?)
- Puppet.info "Found %s in %s" % [node, source]
- return parent, nodeclasses
- else
- # Look for a default node.
- parent, nodeclasses = self.send(method, "default")
- if parent or (nodeclasses and !nodeclasses.empty?)
- Puppet.info "Found default node for %s in %s" %
- [node, source]
- return parent, nodeclasses
- end
- end
- end
- end
- end
+ # That was the first pass evaluation. Now iteratively evaluate
+ # until we've gotten rid of all of everything or thrown an error.
+ evaliterate(scope)
+
+ # Now make sure we fail if there's anything left to do
+ failonleftovers(scope)
- return nil, nil
+ # Now perform the collections
+ scope.collections.each do |coll|
+ coll.evaluate
+ end
+
+ # Now finish everything. This recursively calls finish on the
+ # contained scopes and resources.
+ scope.finish
+
+ # Store everything. We need to do this before translation, because
+ # it operates on resources, not transobjects.
+ if Puppet[:storeconfigs]
+ args = {
+ :resources => scope.resources,
+ :name => client,
+ :facts => facts
+ }
+ unless scope.classlist.empty?
+ args[:classes] = scope.classlist
end
- # Find the ldap node and extra the info, returning just
- # the critical data.
- def nodesearch_ldap(node)
- unless defined? @ldap and @ldap
- setup_ldap()
- unless @ldap
- Puppet.info "Skipping ldap source; no ldap connection"
- return nil, []
- end
- end
+ storeconfigs(args)
+ end
- if node =~ /\./
- node = node.sub(/\..+/, '')
- end
+ # Now, finally, convert our scope tree + resources into a tree of
+ # buckets and objects.
+ objects = scope.translate
- filter = Puppet[:ldapstring]
- attrs = Puppet[:ldapattrs].split("\s*,\s*")
- sattrs = attrs.dup
- pattr = nil
- if pattr = Puppet[:ldapparentattr]
- if pattr == ""
- pattr = nil
- else
- sattrs << pattr
- end
- end
+ # Add the class list
+ unless scope.classlist.empty?
+ objects.classes = scope.classlist
+ end
- if filter =~ /%s/
- filter = filter.gsub(/%s/, node)
- end
+ return objects
+ end
- parent = nil
- classes = []
-
- found = false
- count = 0
- begin
- # We're always doing a sub here; oh well.
- @ldap.search(Puppet[:ldapbase], 2, filter, sattrs) do |entry|
- found = true
- if pattr
- if values = entry.vals(pattr)
- if values.length > 1
- raise Puppet::Error,
- "Node %s has more than one parent: %s" %
- [node, values.inspect]
- end
- unless values.empty?
- parent = values.shift
- end
- end
- end
+ # Fail if there any overrides left to perform.
+ def failonleftovers(scope)
+ overrides = scope.overrides
+ if overrides.empty?
+ return nil
+ else
+ fail Puppet::ParseError,
+ "Could not find object(s) %s" % overrides.collect { |o|
+ o.ref
+ }.join(", ")
+ end
+ end
- attrs.each { |attr|
- if values = entry.vals(attr)
- values.each do |v| classes << v end
- end
- }
- end
- rescue => detail
- if count == 0
- # Try reconnecting to ldap
- @ldap = nil
- setup_ldap()
- retry
- else
- raise Puppet::Error, "LDAP Search failed: %s" % detail
- end
- end
+ # Find a class definition, relative to the current namespace.
+ def findclass(namespace, name)
+ fqfind namespace, name, @classtable
+ end
- classes.flatten!
+ # Find a component definition, relative to the current namespace.
+ def finddefine(namespace, name)
+ fqfind namespace, name, @definetable
+ end
- return parent, classes
- end
+ # The recursive method used to actually look these objects up.
+ def fqfind(namespace, name, table)
+ if name =~ /^::/ or namespace == ""
+ return table[name.sub(/^::/, '')]
+ end
+ ary = namespace.split("::")
- def parsedate
- parsefiles()
- @parsedate
+ while ary.length > 0
+ newname = (ary + [name]).join("::").sub(/^::/, '')
+ if obj = table[newname]
+ return obj
end
- # Add a new file to check for updateness.
- def newfile(file)
- unless @files.find { |f| f.file == file }
- @files << Puppet::LoadedFile.new(file)
- end
- end
+ # Delete the second to last object, which reduces our namespace by one.
+ ary.pop
+ end
- # evaluate our whole tree
- def run(client, facts)
- # We have to leave this for after initialization because there
- # seems to be a problem keeping ldap open after a fork.
- unless @setup
- @nodesources.each { |source|
- method = "setup_%s" % source.to_s
- if respond_to? method
- begin
- self.send(method)
- rescue => detail
- raise Puppet::Error,
- "Could not set up node source %s" % source
- end
- end
- }
- end
- parsefiles()
+ # If we've gotten to this point without finding it, see if the name
+ # exists at the top namespace
+ if obj = table[name]
+ return obj
+ end
+
+ return nil
+ end
+
+ # Create a new node, just from a list of names, classes, and an optional parent.
+ def gennode(name, hash)
+ facts = hash[:facts]
+ classes = hash[:classes]
+ parent = hash[:parentnode]
+ arghash = {
+ :name => name,
+ :code => AST::ASTArray.new(:pin => "[]"),
+ :interp => self,
+ :fqname => name
+ }
+ classes = [classes] unless classes.is_a?(Array)
+
+ classcode = @parser.ast(AST::ASTArray, :children => classes.collect do |klass|
+ @parser.ast(AST::FlatString, :value => klass)
+ end)
+
+ # Now generate a function call.
+ code = @parser.ast(AST::Function,
+ :name => "include",
+ :arguments => classcode,
+ :ftype => :statement
+ )
+
+ if parent
+ arghash[:parentclass] = parent
+ end
+
+ # Create the node
+ return @parser.ast(AST::Node, arghash)
+ end
+
+ # create our interpreter
+ def initialize(hash)
+ if @code = hash[:Code]
+ @file = nil # to avoid warnings
+ elsif ! @file = hash[:Manifest]
+ devfail "You must provide code or a manifest"
+ end
+
+ if hash.include?(:UseNodes)
+ @usenodes = hash[:UseNodes]
+ else
+ @usenodes = true
+ end
+
+ # By default, we only search for parsed nodes.
+ @nodesources = [:code]
- # Really, we should stick multiple names in here
- # but for now just make a simple array
- names = [client]
+ if Puppet[:ldapnodes]
+ # Nodes in the file override nodes in ldap.
+ @nodesources << :ldap
+ end
- # Make sure both the fqdn and the short name of the
- # host can be used in the manifest
- if client =~ /\./
- names << client.sub(/\..+/,'')
+ if hash[:NodeSources]
+ unless hash[:NodeSources].is_a?(Array)
+ hash[:NodeSources] = [hash[:NodeSources]]
+ end
+ hash[:NodeSources].each do |src|
+ if respond_to? "nodesearch_#{src.to_s}"
+ @nodesources << src.to_s.intern
else
- names << "#{client}.#{facts['domain']}"
+ Puppet.warning "Node source '#{src}' not supported"
end
+ end
+ end
- scope = Puppet::Parser::Scope.new() # no parent scope
- scope.name = "top"
- scope.type = "puppet"
- scope.interp = self
+ @setup = false
- classes = @classes.dup
+ initparsevars()
- args = {:ast => @ast, :facts => facts, :classes => classes}
+ # Set it to either the value or nil. This is currently only used
+ # by the cfengine module.
+ @classes = hash[:Classes] || []
- if @usenodes
- unless client
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
+ @local = hash[:Local] || false
- args[:names] = names
+ if hash.include?(:ForkSave)
+ @forksave = hash[:ForkSave]
+ else
+ # This is just too dangerous right now. Sorry, it's going
+ # to have to be slow.
+ @forksave = false
+ end
+
+ # The class won't always be defined during testing.
+ if Puppet[:storeconfigs] and defined? ActiveRecord::Base
+ Puppet::Rails.init
+ end
+
+ @files = []
+
+ # Create our parser object
+ parsefiles
+ end
+
+ # Initialize or reset the variables related to parsing.
+ def initparsevars
+ @classtable = {}
+ @namespace = "main"
+
+ @nodetable = {}
+
+ @definetable = {}
+ end
+
+ # Find the ldap node and extra the info, returning just
+ # the critical data.
+ def ldapsearch(node)
+ unless defined? @ldap and @ldap
+ setup_ldap()
+ unless @ldap
+ Puppet.info "Skipping ldap source; no ldap connection"
+ return nil, []
+ end
+ end
- parent, nodeclasses = nodesearch(*names)
+ if node =~ /\./
+ node = node.sub(/\..+/, '')
+ end
- args[:classes] += nodeclasses if nodeclasses
+ filter = Puppet[:ldapstring]
+ attrs = Puppet[:ldapattrs].split("\s*,\s*")
+ sattrs = attrs.dup
+ pattr = nil
+ if pattr = Puppet[:ldapparentattr]
+ if pattr == ""
+ pattr = nil
+ else
+ sattrs << pattr
+ end
+ end
- args[:parentnode] = parent if parent
+ if filter =~ /%s/
+ filter = filter.gsub(/%s/, node)
+ end
- if nodeclasses or parent
- args[:searched] = true
+ parent = nil
+ classes = []
+
+ found = false
+ count = 0
+ begin
+ # We're always doing a sub here; oh well.
+ @ldap.search(Puppet[:ldapbase], 2, filter, sattrs) do |entry|
+ found = true
+ if pattr
+ if values = entry.vals(pattr)
+ if values.length > 1
+ raise Puppet::Error,
+ "Node %s has more than one parent: %s" %
+ [node, values.inspect]
+ end
+ unless values.empty?
+ parent = values.shift
+ end
end
end
- begin
- objects = scope.evaluate(args)
- rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except
- raise
- rescue => except
- error = Puppet::DevError.new("%s: %s" %
- [except.class, except.message])
- error.set_backtrace except.backtrace
- raise error
- end
+ attrs.each { |attr|
+ if values = entry.vals(attr)
+ values.each do |v| classes << v end
+ end
+ }
+ end
+ rescue => detail
+ if count == 0
+ # Try reconnecting to ldap
+ @ldap = nil
+ setup_ldap()
+ retry
+ else
+ raise Puppet::Error, "LDAP Search failed: %s" % detail
+ end
+ end
- if Puppet[:storeconfigs]
- storeconfigs(
- :objects => objects,
- :host => client,
- :facts => facts
- )
- end
+ classes.flatten!
+
+ return parent, classes
+ end
+
+ # Split an fq name into a namespace and name
+ def namesplit(fullname)
+ ary = fullname.split("::")
+ n = ary.pop || ""
+ ns = ary.join("::")
+ return ns, n
+ end
- return objects
+ # Create a new class, or merge with an existing class.
+ def newclass(fqname, options = {})
+ if @definetable.include?(fqname)
+ raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
+ fqname
+ end
+ code = options[:code]
+ parent = options[:parent]
+
+ # If the class is already defined, then add code to it.
+ if other = @classtable[fqname]
+ # Make sure the parents match
+ if parent and other.parentclass and (parent != other.parentclass)
+ @parser.error @parser.addcontext("Class %s is already defined" % fqname) +
+ " with parent %s" % [fqname, other.parentclass]
end
- # Connect to the LDAP Server
- def setup_ldap
- self.class.ldap = nil
- begin
- require 'ldap'
- rescue LoadError
- Puppet.notice(
- "Could not set up LDAP Connection: Missing ruby/ldap libraries"
- )
- @ldap = nil
- return
+ # This might be dangerous...
+ if parent and ! other.parentclass
+ other.parentclass = parent
+ end
+
+ # This might just be an empty, stub class.
+ if code
+ tmp = fqname
+ if tmp == ""
+ tmp = "main"
end
- begin
- @ldap = self.class.ldap()
- rescue => detail
- raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+
+ Puppet.debug @parser.addcontext("Adding code to %s" % tmp)
+ # Else, add our code to it.
+ if other.code and code
+ other.code.children += code.children
+ else
+ other.code ||= code
end
end
+ else
+ # Define it anew.
+ ns, name = namesplit(fqname)
+ args = {:type => name, :namespace => ns, :fqname => fqname, :interp => self}
+ args[:code] = code if code
+ args[:parentclass] = parent if parent
+ @classtable[fqname] = @parser.ast AST::HostClass, args
+ end
- def scope
- return @scope
- end
+ return @classtable[fqname]
+ end
- private
+ # Create a new definition.
+ def newdefine(fqname, options = {})
+ if @classtable.include?(fqname)
+ raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
+ fqname
+ end
+ # Make sure our definition doesn't already exist
+ if other = @definetable[fqname]
+ @parser.error @parser.addcontext(
+ "%s is already defined at line %s" % [fqname, other.line],
+ other
+ )
+ end
- # Check whether any of our files have changed.
- def checkfiles
- if @files.find { |f| f.changed? }
- @parsedate = Time.now.to_i
- end
+ ns, name = namesplit(fqname)
+ args = {
+ :type => name,
+ :namespace => ns,
+ :arguments => options[:arguments],
+ :code => options[:code],
+ :fqname => fqname
+ }
+
+ [:code, :arguments].each do |param|
+ args[param] = options[param] if options[param]
+ end
+
+ @definetable[fqname] = @parser.ast AST::Component, args
+ end
+
+ # Create a new node. Nodes are special, because they're stored in a global
+ # table, not according to namespaces.
+ def newnode(names, options = {})
+ names = [names] unless names.instance_of?(Array)
+ names.collect do |name|
+ if other = @nodetable[name]
+ @parser.error @parser.addcontext("Node %s is already defined" % [other.name], other)
+ end
+ name = name.to_s if name.is_a?(Symbol)
+ args = {
+ :name => name,
+ }
+ if options[:code]
+ args[:code] = options[:code]
+ end
+ if options[:parent]
+ args[:parentclass] = options[:parent]
end
+ @nodetable[name] = @parser.ast(AST::Node, args)
+ @nodetable[name].fqname = name
+ @nodetable[name]
+ @nodetable[name].interp = self
+ @nodetable[name]
+ end
+ end
+
+ # Add a new file to be checked when we're checking to see if we should be
+ # reparsed.
+ def newfile(*files)
+ files.each do |file|
+ unless file.is_a? Puppet::LoadedFile
+ file = Puppet::LoadedFile.new(file)
+ end
+ @files << file
+ end
+ end
- # Parse the files, generating our parse tree. This automatically
- # reparses only if files are updated, so it's safe to call multiple
- # times.
- def parsefiles
- # First check whether there are updates to any non-puppet files
- # like templates. If we need to reparse, this will get quashed,
- # but it needs to be done first in case there's no reparse
- # but there are other file changes.
- checkfiles()
-
- # Check if the parser should reparse.
- if @file
- if defined? @parser
- if stamp = @parser.reparse?
- Puppet.notice "Reloading files"
- else
- return false
+ # Search for our node in the various locations.
+ def nodesearch(*nodes)
+ # At this point, stop at the first source that defines
+ # the node
+ @nodesources.each do |source|
+ method = "nodesearch_%s" % source
+ if self.respond_to? method
+ # Do an inverse sort on the length, so the longest match always
+ # wins
+ nodes.sort { |a,b| b.length <=> a.length }.each do |node|
+ node = node.to_s if node.is_a?(Symbol)
+ if obj = self.send(method, node)
+ nsource = obj.file || source
+ Puppet.info "Found %s in %s" % [node, nsource]
+ return obj
+ else
+ # Look for a default node.
+ if defobj = self.send(method, "default")
+ Puppet.info "Found default node for %s in %s" %
+ [node, source]
+ return defobj
end
end
+ end
+ end
+ end
- unless FileTest.exists?(@file)
- if @ast
- return
- else
- raise Puppet::Error, "Manifest %s must exist" % @file
- end
+ return nil
+ end
+
+ # See if our node was defined in the code.
+ def nodesearch_code(name)
+ @nodetable[name]
+ end
+
+ # Look for our node in ldap.
+ def nodesearch_ldap(node)
+ parent, classes = ldapsearch(node)
+ if parent or classes
+ args = {}
+ args[:classes] = classes if classes and ! classes.empty?
+ args[:parentnode] = parent if parent
+ return gennode(node, args)
+ else
+ return nil
+ end
+ end
+
+ def parsedate
+ parsefiles()
+ @parsedate
+ end
+
+ # evaluate our whole tree
+ def run(client, facts)
+ # We have to leave this for after initialization because there
+ # seems to be a problem keeping ldap open after a fork.
+ unless @setup
+ @nodesources.each { |source|
+ method = "setup_%s" % source.to_s
+ if respond_to? method
+ exceptwrap :type => Puppet::Error,
+ :message => "Could not set up node source %s" % source do
+ self.send(method)
end
end
+ }
+ end
+ parsefiles()
+
+ # Evaluate all of the appropriate code.
+ objects = evaluate(client, facts)
+
+ # And return it all.
+ return objects
+ end
+
+ # Connect to the LDAP Server
+ def setup_ldap
+ self.class.ldap = nil
+ begin
+ require 'ldap'
+ rescue LoadError
+ Puppet.notice(
+ "Could not set up LDAP Connection: Missing ruby/ldap libraries"
+ )
+ @ldap = nil
+ return
+ end
+ begin
+ @ldap = self.class.ldap()
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+
+ def scope
+ return @scope
+ end
+
+ # Iteratively make sure that every object in the scope tree is translated.
+ def translate(scope)
+ end
+
+ private
- # should i be creating a new parser each time...?
- @parser = Puppet::Parser::Parser.new()
- if @code
- @parser.string = @code
+ # Check whether any of our files have changed.
+ def checkfiles
+ if @files.find { |f| f.changed? }
+ @parsedate = Time.now.to_i
+ end
+ end
+
+ # Parse the files, generating our parse tree. This automatically
+ # reparses only if files are updated, so it's safe to call multiple
+ # times.
+ def parsefiles
+ # First check whether there are updates to any non-puppet files
+ # like templates. If we need to reparse, this will get quashed,
+ # but it needs to be done first in case there's no reparse
+ # but there are other file changes.
+ checkfiles()
+
+ # Check if the parser should reparse.
+ if @file
+ if defined? @parser
+ if stamp = @parser.reparse?
+ Puppet.notice "Reloading files"
else
- @parser.file = @file
- # Mark when we parsed, so we can check freshness
- @parsedate = File.stat(@file).ctime.to_i
+ return false
end
+ end
- if @local
- @ast = @parser.parse
+ unless FileTest.exists?(@file)
+ # If we've already parsed, then we're ok.
+ if findclass("", "")
+ return
else
- benchmark(:info, "Parsed manifest") do
- @ast = @parser.parse
- end
+ raise Puppet::Error, "Manifest %s must exist" % @file
end
- @parsedate = Time.now.to_i
end
+ end
- # Store the configs into the database.
- def storeconfigs(hash)
- unless defined? ActiveRecord
- require 'puppet/rails'
- unless defined? ActiveRecord
- raise LoadError,
- "storeconfigs is enabled but rails is unavailable"
- end
- end
+ # Reset our parse tables.
+ clear()
+
+ # Create a new parser, just to keep things fresh.
+ @parser = Puppet::Parser::Parser.new(self)
+ if @code
+ @parser.string = @code
+ else
+ @parser.file = @file
+ # Mark when we parsed, so we can check freshness
+ @parsedate = File.stat(@file).ctime.to_i
+ end
- Puppet::Rails.init
-
- # Fork the storage, since we don't need the client waiting
- # on that. How do I avoid this duplication?
- if @forksave
- fork {
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:client]}") do
- # Try to batch things a bit, by putting them into
- # a transaction
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
- end
- }
- else
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:client]}") do
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
+ # Parsing stores all classes and defines and such in their
+ # various tables, so we don't worry about the return.
+ if @local
+ @parser.parse
+ else
+ benchmark(:info, "Parsed manifest") do
+ @parser.parse
+ end
+ end
+ @parsedate = Time.now.to_i
+ end
+
+ # Store the configs into the database.
+ def storeconfigs(hash)
+ unless defined? ActiveRecord
+ require 'puppet/rails'
+ unless defined? ActiveRecord
+ raise LoadError,
+ "storeconfigs is enabled but rails is unavailable"
+ end
+ end
+
+ Puppet::Rails.init
+
+ # Fork the storage, since we don't need the client waiting
+ # on that. How do I avoid this duplication?
+ if @forksave
+ fork {
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{hash[:name]}") do
+ # Try to batch things a bit, by putting them into
+ # a transaction
+ Puppet::Rails::Host.transaction do
+ Puppet::Rails::Host.store(hash)
end
end
-
- # Now that we've stored everything, we need to strip out
- # the collectable objects so that they are not sent on
- # to the host
- hash[:objects].collectstrip!
+ }
+ else
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{hash[:name]}") do
+ Puppet::Rails::Host.transaction do
+ Puppet::Rails::Host.store(hash)
+ end
end
end
+
+ # Now that we've stored everything, we need to strip out
+ # the collectable objects so that they are not sent on
+ # to the host
+ #hash[:objects].collectstrip!
end
end
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
index 80a8715ba..39fef8668 100644
--- a/lib/puppet/parser/lexer.rb
+++ b/lib/puppet/parser/lexer.rb
@@ -12,6 +12,8 @@ module Puppet
class Lexer
attr_reader :line, :last, :file
+ attr_accessor :indefine
+
#%r{\w+} => :WORD,
@@tokens = {
%r{#.*} => :COMMENT,
@@ -31,10 +33,13 @@ module Puppet
%r{<} => :LESSTHAN,
%r{<=} => :LESSEQUAL,
%r{!=} => :NOTEQUAL,
+ %r{!} => :NOT,
%r{,} => :COMMA,
%r{\.} => :DOT,
%r{:} => :COLON,
%r{@} => :AT,
+ %r{<<\|} => :LLCOLLECT,
+ %r{\|>>} => :RRCOLLECT,
%r{<\|} => :LCOLLECT,
%r{\|>} => :RCOLLECT,
%r{;} => :SEMIC,
@@ -42,6 +47,8 @@ module Puppet
%r{\\} => :BACKSLASH,
%r{=>} => :FARROW,
%r{[a-z][-\w]*} => :NAME,
+ %r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME,
+ %r{([A-Z][-\w]*::)+[A-Z][-\w]*} => :CLASSREF,
%r{[A-Z][-\w]*} => :TYPE,
%r{[0-9]+} => :NUMBER,
%r{\$\w+} => :VARIABLE
@@ -59,9 +66,15 @@ module Puppet
"else" => :ELSE,
"inherits" => :INHERITS,
"node" => :NODE,
- "true" => :BOOLEAN
+ "true" => :BOOLEAN,
+ "and" => :AND,
+ "or" => :OR
}
+ def clear
+ initvars
+ end
+
# scan the whole file
# basically just used for testing
def fullscan
@@ -90,14 +103,47 @@ module Puppet
}
end
+ def indefine?
+ if defined? @indefine
+ @indefine
+ else
+ false
+ end
+ end
+
def initialize
+ initvars()
+ end
+
+ def initvars
@line = 1
@last = ""
+ @lasttoken = nil
@scanner = nil
@file = nil
# AAARRGGGG! okay, regexes in ruby are bloody annoying
# no one else has "\n" =~ /\s/
@skip = %r{[ \t]+}
+
+ @namestack = []
+ @indefine = false
+ end
+
+ # Go up one in the namespace.
+ def namepop
+ @namestack.pop
+ end
+
+ # Collect the current namespace.
+ def namespace
+ @namestack.join("::")
+ end
+
+ # This value might have :: in it, but we don't care -- it'll be
+ # handled normally when joining, and when popping we want to pop
+ # this full value, however long the namespace is.
+ def namestack(value)
+ @namestack << value
end
def rest
@@ -164,6 +210,7 @@ module Puppet
# if this gets much more complicated, it should
# be moved up to where the tokens themselves are defined
# which will get me about 75% of the way to a lexer generator
+ ptoken = stoken
case stoken
when :NAME then
wtoken = stoken
@@ -171,36 +218,51 @@ module Puppet
if @@keywords.include?(value)
wtoken = @@keywords[value]
#Puppet.debug("token '%s'" % wtoken)
+ if wtoken == :BOOLEAN
+ value = eval(value)
+ end
end
- yield [wtoken,value]
- @last = value
+ ptoken = wtoken
when :NUMBER then
- yield [:NAME,value]
- # just throw comments away
+ ptoken = :NAME
when :COMMENT then
# just throw comments away
+ next
when :RETURN then
@line += 1
@scanner.skip(@skip)
+ next
when :SQUOTE then
#Puppet.debug("searching '%s' after '%s'" % [self.rest,value])
value = self.slurpstring(value)
- yield [:SQTEXT,value]
- @last = value
- #stoken = :DQTEXT
+ ptoken = :SQTEXT
#Puppet.debug("got string '%s' => '%s'" % [:DQTEXT,value])
when :DQUOTE then
- #Puppet.debug("searching '%s' after '%s'" % [self.rest,value])
value = self.slurpstring(value)
- yield [:DQTEXT,value]
- @last = value
- #stoken = :DQTEXT
- #Puppet.debug("got string '%s' => '%s'" % [:DQTEXT,value])
- else
- #Puppet.debug("got token '%s' => '%s'" % [stoken,value])
- yield [stoken,value]
- @last = value
+ ptoken = :DQTEXT
+ when :VARIABLE then
+ value = value.sub(/^\$/, '')
+ end
+
+ yield [ptoken, value]
+
+ if @lasttoken == :CLASS
+ namestack(value)
end
+
+ if @lasttoken == :DEFINE
+ if indefine?
+ self.indefine = false
+ raise Puppet::ParseError,
+ "Definitions cannot nest"
+ end
+
+ @indefine = true
+ end
+
+ @last = value
+ @lasttoken = ptoken
+
@scanner.skip(@skip)
end
@scanner = nil
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index 3cda43ee0..047dace3a 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
@@ -29,12 +29,24 @@ module Puppet
class Parser < Racc::Parser
-module_eval <<'..end grammar.ra modeval..idc4b6d943e3', 'grammar.ra', 603
+module_eval <<'..end grammar.ra modeval..ide506d3a623', 'grammar.ra', 621
require 'puppet/parser/functions'
-attr_reader :file
+attr_reader :file, :interp
attr_accessor :files
+# Add context to a message; useful for error messages and such.
+def addcontext(message, obj = nil)
+ obj ||= @lexer
+
+ message += " on line %s" % obj.line
+ if file = obj.file
+ message += " in file %s" % file
+ end
+
+ return message
+end
+
# Create an AST array out of all of the args
def aryfy(*args)
if args[0].instance_of?(AST::ASTArray)
@@ -66,6 +78,17 @@ def ast(klass, hash = nil)
return klass.new(hash)
end
+# Raise a Parse error.
+def error(message)
+ except = Puppet::ParseError.new(message)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
def file=(file)
unless FileTest.exists?(file)
unless file =~ /\.pp$/
@@ -83,23 +106,20 @@ def file=(file)
end
end
-def initialize
+def initialize(interpreter)
+ @interp = interpreter
+ initvars()
+end
+
+# Initialize or reset all of our variables.
+def initvars
@lexer = Puppet::Parser::Lexer.new()
@files = []
- #if Puppet[:debug]
- # @yydebug = true
- #end
end
-# Add a new file to be checked when we're checking to see if we should be
-# reparsed.
-def newfile(*files)
- files.each do |file|
- unless file.is_a? Puppet::LoadedFile
- file = Puppet::LoadedFile.new(file)
- end
- @files << file
- end
+# The fully qualifed name, with the full namespace.
+def fqname(name)
+ [@lexer.namespace, name].join("::").sub(/^::/, '')
end
def on_error(token,value,stack)
@@ -123,7 +143,7 @@ def parse(string = nil)
self.string = string
end
begin
- yyparse(@lexer,:scan)
+ main = yyparse(@lexer,:scan)
rescue Racc::ParseError => except
error = Puppet::ParseError.new(except)
error.line = @lexer.line
@@ -150,6 +170,13 @@ def parse(string = nil)
error.set_backtrace except.backtrace
raise error
end
+ if main
+ # Store the results as the top-level class.
+ interp.newclass("", :code => main)
+ return main
+ end
+ensure
+ @lexer.clear
end
# See if any of the files have changed.
@@ -172,363 +199,413 @@ end
# $Id$
-..end grammar.ra modeval..idc4b6d943e3
+..end grammar.ra modeval..ide506d3a623
##### racc 1.4.5 generates ###
racc_reduce_table = [
0, 0, :racc_error,
- 1, 44, :_reduce_1,
- 1, 44, :_reduce_none,
- 1, 45, :_reduce_none,
- 2, 45, :_reduce_4,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 1, 47, :_reduce_none,
- 4, 55, :_reduce_16,
- 2, 55, :_reduce_17,
- 1, 59, :_reduce_none,
- 3, 59, :_reduce_19,
- 1, 60, :_reduce_none,
- 1, 60, :_reduce_none,
- 1, 60, :_reduce_none,
- 5, 48, :_reduce_23,
- 5, 48, :_reduce_24,
- 5, 48, :_reduce_25,
- 2, 49, :_reduce_26,
- 3, 50, :_reduce_27,
- 4, 69, :_reduce_28,
- 1, 64, :_reduce_none,
- 3, 64, :_reduce_30,
- 0, 65, :_reduce_none,
- 1, 65, :_reduce_none,
- 1, 61, :_reduce_33,
- 1, 68, :_reduce_34,
- 1, 70, :_reduce_none,
- 1, 70, :_reduce_none,
- 1, 70, :_reduce_none,
- 1, 70, :_reduce_none,
- 1, 70, :_reduce_none,
- 1, 70, :_reduce_none,
- 3, 51, :_reduce_41,
- 0, 66, :_reduce_42,
- 1, 66, :_reduce_43,
- 3, 66, :_reduce_44,
- 3, 74, :_reduce_45,
- 1, 75, :_reduce_none,
- 3, 75, :_reduce_47,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 1, 73, :_reduce_none,
- 4, 79, :_reduce_57,
- 1, 63, :_reduce_58,
- 1, 63, :_reduce_59,
- 1, 77, :_reduce_60,
- 4, 78, :_reduce_61,
- 6, 53, :_reduce_62,
- 0, 81, :_reduce_none,
- 4, 81, :_reduce_64,
+ 1, 51, :_reduce_1,
+ 1, 51, :_reduce_none,
+ 1, 52, :_reduce_none,
+ 2, 52, :_reduce_4,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 4, 62, :_reduce_17,
+ 2, 62, :_reduce_18,
+ 1, 67, :_reduce_none,
+ 3, 67, :_reduce_20,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 5, 55, :_reduce_24,
+ 5, 55, :_reduce_25,
+ 5, 55, :_reduce_26,
+ 4, 66, :_reduce_27,
+ 2, 56, :_reduce_28,
+ 1, 77, :_reduce_29,
+ 2, 77, :_reduce_30,
+ 2, 57, :_reduce_31,
+ 1, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 3, 79, :_reduce_34,
+ 3, 79, :_reduce_35,
+ 1, 80, :_reduce_none,
1, 80, :_reduce_none,
- 5, 52, :_reduce_66,
+ 3, 80, :_reduce_38,
+ 1, 81, :_reduce_none,
+ 3, 81, :_reduce_40,
1, 82, :_reduce_none,
- 2, 82, :_reduce_68,
- 5, 83, :_reduce_69,
- 4, 83, :_reduce_70,
+ 1, 82, :_reduce_none,
+ 3, 83, :_reduce_43,
+ 3, 83, :_reduce_44,
+ 1, 84, :_reduce_none,
1, 84, :_reduce_none,
- 3, 84, :_reduce_72,
- 3, 71, :_reduce_73,
- 1, 86, :_reduce_none,
- 3, 86, :_reduce_75,
- 1, 88, :_reduce_none,
- 3, 88, :_reduce_77,
- 3, 87, :_reduce_78,
+ 4, 86, :_reduce_47,
+ 1, 72, :_reduce_none,
+ 3, 72, :_reduce_49,
+ 0, 73, :_reduce_none,
+ 1, 73, :_reduce_none,
+ 1, 69, :_reduce_52,
+ 1, 88, :_reduce_53,
+ 1, 87, :_reduce_none,
+ 1, 87, :_reduce_none,
+ 1, 87, :_reduce_none,
+ 1, 87, :_reduce_none,
+ 1, 87, :_reduce_none,
+ 1, 87, :_reduce_none,
+ 3, 58, :_reduce_60,
+ 0, 74, :_reduce_61,
+ 1, 74, :_reduce_62,
+ 3, 74, :_reduce_63,
+ 3, 92, :_reduce_64,
+ 1, 93, :_reduce_none,
+ 3, 93, :_reduce_66,
1, 85, :_reduce_none,
1, 85, :_reduce_none,
1, 85, :_reduce_none,
1, 85, :_reduce_none,
1, 85, :_reduce_none,
1, 85, :_reduce_none,
- 1, 85, :_reduce_85,
- 2, 54, :_reduce_86,
- 6, 56, :_reduce_87,
- 5, 56, :_reduce_88,
- 6, 57, :_reduce_89,
- 5, 57, :_reduce_90,
- 6, 58, :_reduce_91,
- 5, 58, :_reduce_92,
1, 91, :_reduce_none,
- 3, 91, :_reduce_94,
- 1, 92, :_reduce_95,
- 1, 92, :_reduce_96,
- 1, 92, :_reduce_97,
- 0, 46, :_reduce_98,
- 1, 89, :_reduce_none,
- 3, 89, :_reduce_100,
- 3, 89, :_reduce_101,
- 1, 93, :_reduce_none,
- 3, 93, :_reduce_103,
- 3, 94, :_reduce_104,
- 1, 94, :_reduce_105,
- 3, 94, :_reduce_106,
- 1, 94, :_reduce_107,
- 1, 90, :_reduce_none,
- 2, 90, :_reduce_109,
- 1, 62, :_reduce_110,
- 1, 95, :_reduce_111,
- 3, 72, :_reduce_112,
- 2, 72, :_reduce_113,
- 1, 76, :_reduce_none,
- 1, 76, :_reduce_none,
- 0, 67, :_reduce_none,
- 1, 67, :_reduce_117 ]
-
-racc_reduce_n = 118
-
-racc_shift_n = 197
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 4, 96, :_reduce_82,
+ 1, 71, :_reduce_83,
+ 1, 71, :_reduce_84,
+ 1, 95, :_reduce_85,
+ 4, 76, :_reduce_86,
+ 4, 76, :_reduce_87,
+ 6, 60, :_reduce_88,
+ 0, 98, :_reduce_none,
+ 4, 98, :_reduce_90,
+ 1, 97, :_reduce_none,
+ 5, 59, :_reduce_92,
+ 1, 99, :_reduce_none,
+ 2, 99, :_reduce_94,
+ 5, 100, :_reduce_95,
+ 4, 100, :_reduce_96,
+ 1, 101, :_reduce_none,
+ 3, 101, :_reduce_98,
+ 3, 89, :_reduce_99,
+ 1, 103, :_reduce_none,
+ 3, 103, :_reduce_101,
+ 1, 105, :_reduce_none,
+ 3, 105, :_reduce_103,
+ 3, 104, :_reduce_104,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_none,
+ 1, 102, :_reduce_111,
+ 2, 61, :_reduce_112,
+ 6, 63, :_reduce_113,
+ 5, 63, :_reduce_114,
+ 6, 64, :_reduce_115,
+ 5, 64, :_reduce_116,
+ 6, 65, :_reduce_117,
+ 5, 65, :_reduce_118,
+ 1, 106, :_reduce_none,
+ 1, 106, :_reduce_none,
+ 1, 109, :_reduce_none,
+ 3, 109, :_reduce_122,
+ 1, 110, :_reduce_none,
+ 1, 110, :_reduce_none,
+ 1, 110, :_reduce_none,
+ 0, 53, :_reduce_126,
+ 0, 111, :_reduce_127,
+ 1, 107, :_reduce_none,
+ 3, 107, :_reduce_129,
+ 3, 107, :_reduce_130,
+ 1, 112, :_reduce_none,
+ 3, 112, :_reduce_132,
+ 3, 113, :_reduce_133,
+ 1, 113, :_reduce_134,
+ 3, 113, :_reduce_135,
+ 1, 113, :_reduce_136,
+ 1, 108, :_reduce_none,
+ 2, 108, :_reduce_138,
+ 1, 70, :_reduce_139,
+ 3, 90, :_reduce_140,
+ 2, 90, :_reduce_141,
+ 1, 94, :_reduce_none,
+ 1, 94, :_reduce_none,
+ 0, 75, :_reduce_none,
+ 1, 75, :_reduce_145 ]
+
+racc_reduce_n = 146
+
+racc_shift_n = 240
racc_action_table = [
- 48, 36, 38, 81, 162, 20, 48, 36, 38, 64,
- 79, 161, 48, 36, 38, 86, 20, 36, 38, 75,
- 36, 38, 20, 37, -79, 145, 36, 38, 20, 44,
- 37, -82, -79, 49, 53, 44, 31, 55, 31, 49,
- 53, 44, 72, 55, 65, 49, 53, -81, 44, 55,
- 48, 36, 38, 37, 44, 145, 48, 36, 38, 37,
- 37, 137, 48, 36, 38, 113, 20, 139, 29, 79,
- 29, 33, 20, 33, -80, 79, 85, 170, 20, 44,
- 154, 146, 102, 49, 53, 44, 115, 55, 171, 49,
- 53, 44, 149, 55, 77, 49, 53, 36, 38, 55,
- 48, 36, 38, 36, 38, 116, 48, 36, 38, 117,
- 118, 88, 48, 36, 38, 179, 20, 117, 118, -81,
- 45, 87, 20, 112, 155, 44, -82, 158, 20, 44,
- 37, 44, 105, 49, 53, 44, 37, 55, 64, 49,
- 53, 44, 163, 55, 77, 49, 53, 85, 84, 55,
- 48, 36, 38, -80, 169, 72, 48, 36, 38, 172,
- 173, -83, 48, 36, 38, -84, 20, 178, 108, 136,
- 182, 77, 20, 36, 38, 112, 71, 160, 20, 44,
- 109, 70, 69, 49, 53, 44, 113, 55, 20, 49,
- 53, 44, 190, 55, 66, 49, 93, 36, 38, 55,
- 112, 44, 35, 28, 166, 49, 53, 36, 38, 55,
- 125, nil, 20, 36, 38, nil, nil, nil, nil, 36,
- 38, nil, 20, nil, nil, 44, nil, nil, 20, 49,
- 53, nil, nil, 55, 20, 44, nil, nil, nil, 49,
- 53, 44, nil, 55, nil, 49, 53, 44, nil, 55,
- nil, 49, 53, 36, 38, 55, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 20, 176,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 20, 44, nil, nil, 168, 49, 53, nil, 12, 55,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, 175,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, 196, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, 153,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, 185, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, 189,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, 195, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, 148,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, 193, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, nil,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, nil, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, nil,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- 20, 3, nil, 9, nil, 14, nil, 21, 12, nil,
- 16, 19, nil, 24, 26, 20, 3, nil, 9, nil,
- 14, nil, 21, 12, nil, 16, 19, nil, 24, 26,
- nil, 3, nil, 9, nil, 14, nil, 21 ]
+ 59, 50, 52, 50, 52, 97, 59, 50, 52, 33,
+ 146, 75, 59, 50, 52, 76, 69, -105, 59, 50,
+ 52, 171, 111, 120, 59, 50, 52, 117, 69, 32,
+ 84, 45, 38, 45, 69, 64, 67, 45, 54, 70,
+ 69, 64, 108, 45, 146, 70, 137, 64, 67, 45,
+ 150, 70, 121, 64, 67, 45, 39, 70, 178, 64,
+ 67, 50, 52, 70, 59, 50, 52, 50, 52, 122,
+ 59, 50, 52, 48, 134, 135, 59, 50, 52, 75,
+ 69, 99, 59, 50, 52, -110, 69, 124, 59, 50,
+ 52, 45, 69, 120, 143, 45, 54, 45, 69, 64,
+ 67, 45, 54, 70, 69, 64, 67, 45, 126, 70,
+ -107, 64, 67, 45, -108, 70, 139, 64, 67, 45,
+ 183, 70, 87, 64, 67, 138, 99, 70, 59, 50,
+ 52, -105, 173, 45, 59, 50, 52, 177, 54, 205,
+ 59, 50, 52, 119, 69, 134, 135, 50, 52, 197,
+ 69, 208, 173, 50, 52, 179, 111, 177, 180, 45,
+ 41, 215, 111, 64, 67, 45, 198, 70, 111, 64,
+ 67, 45, 41, 70, 214, 64, 163, 45, 124, 70,
+ 124, 64, 163, 45, -106, 70, 210, 64, 163, 50,
+ 52, 70, 35, 209, 42, 50, 52, 43, 202, 34,
+ 35, 120, 50, 52, 111, 164, 42, 34, 99, 43,
+ 111, 50, 52, 50, 52, 50, 52, 111, 150, 45,
+ 50, 52, 147, 64, 163, 45, 111, 70, 111, 64,
+ 163, -107, 45, 70, 152, 111, 64, 163, 154, 155,
+ 70, 45, -108, 45, 211, 64, 163, 64, 163, 70,
+ 45, 70, 212, 87, 64, 163, 87, 87, 70, 48,
+ 50, 52, 213, 56, 45, 219, 140, 45, 45, 54,
+ 134, 135, 54, 54, -105, 224, 17, 154, 155, 46,
+ 216, 84, 206, 124, 76, -106, 14, -109, 20, 23,
+ 45, 1, 5, 17, 9, 54, 12, -108, 16, 217,
+ 24, -107, -110, 14, 56, 20, 23, 150, 1, 5,
+ 17, 9, 82, 12, -106, 16, 220, 24, 225, 227,
+ 14, 81, 20, 23, 145, 1, 5, 17, 9, 127,
+ 12, 124, 16, 232, 24, 77, 141, 14, 234, 20,
+ 23, 131, 1, 5, 17, 9, 132, 12, 30, 16,
+ 235, 24, 151, nil, 14, nil, 20, 23, nil, 1,
+ 5, 17, 9, nil, 12, nil, 16, 193, 24, nil,
+ nil, 14, nil, 20, 23, nil, 1, 5, 17, 9,
+ nil, 12, nil, 16, 238, 24, nil, nil, 14, nil,
+ 20, 23, nil, 1, 5, 17, 9, nil, 12, nil,
+ 16, 181, 24, nil, nil, 14, nil, 20, 23, nil,
+ 1, 5, 17, 9, nil, 12, nil, 16, 239, 24,
+ nil, nil, 14, nil, 20, 23, nil, 1, 5, 17,
+ 9, nil, 12, nil, 16, nil, 24, nil, nil, 14,
+ nil, 20, 23, nil, 1, 5, 17, 9, nil, 12,
+ nil, 16, nil, 24, nil, nil, 14, nil, 20, 23,
+ nil, 1, 5, 17, 9, nil, 12, nil, 16, nil,
+ 24, nil, nil, 14, nil, 20, 23, nil, 1, 5,
+ 17, 9, nil, 12, nil, 16, nil, 24, nil, nil,
+ 14, nil, 20, 23, nil, 1, 5, 17, 9, nil,
+ 12, nil, 16, nil, 24, nil, nil, 14, nil, 20,
+ 23, nil, 1, 5, nil, 9, nil, 12, nil, 16,
+ nil, 24 ]
racc_action_check = [
- 48, 48, 48, 48, 133, 21, 86, 86, 86, 17,
- 39, 133, 137, 137, 137, 57, 48, 16, 16, 30,
- 79, 79, 86, 21, 98, 170, 45, 45, 137, 48,
- 170, 99, 57, 48, 48, 86, 3, 48, 75, 86,
- 86, 137, 30, 86, 17, 137, 137, 100, 79, 137,
- 69, 69, 69, 79, 45, 105, 119, 119, 119, 45,
- 105, 101, 172, 172, 172, 93, 69, 103, 3, 121,
- 75, 3, 119, 75, 92, 80, 93, 141, 172, 69,
- 121, 107, 65, 69, 69, 119, 80, 69, 141, 119,
- 119, 172, 111, 119, 112, 172, 172, 85, 85, 172,
- 12, 12, 12, 9, 9, 83, 14, 14, 14, 83,
- 83, 62, 113, 113, 113, 156, 12, 156, 156, 61,
- 9, 60, 14, 90, 122, 85, 59, 128, 113, 12,
- 85, 9, 66, 12, 12, 14, 9, 12, 68, 14,
- 14, 113, 135, 14, 136, 113, 113, 53, 52, 113,
- 173, 173, 173, 51, 140, 70, 158, 158, 158, 142,
- 144, 50, 64, 64, 64, 46, 173, 153, 72, 95,
- 161, 35, 158, 131, 131, 164, 28, 131, 64, 173,
- 74, 26, 24, 173, 173, 158, 77, 173, 131, 158,
- 158, 64, 178, 158, 19, 64, 64, 88, 88, 64,
- 76, 131, 6, 2, 138, 131, 131, 87, 87, 131,
- 87, nil, 88, 125, 125, nil, nil, nil, nil, 162,
- 162, nil, 87, nil, nil, 88, nil, nil, 125, 88,
- 88, nil, nil, 88, 162, 87, nil, nil, nil, 87,
- 87, 125, nil, 87, nil, 125, 125, 162, nil, 125,
- nil, 162, 162, 180, 180, 162, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, 180, 147,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- 147, 180, nil, nil, 139, 180, 180, nil, 147, 180,
- 147, 147, nil, 147, 147, 139, 147, nil, 147, 146,
- 147, nil, 147, 139, nil, 139, 139, nil, 139, 139,
- 146, 139, nil, 139, 194, 139, nil, 139, 146, nil,
- 146, 146, nil, 146, 146, 194, 146, nil, 146, 120,
- 146, nil, 146, 194, nil, 194, 194, nil, 194, 194,
- 120, 194, nil, 194, 167, 194, nil, 194, 120, nil,
- 120, 120, nil, 120, 120, 167, 120, nil, 120, 174,
- 120, nil, 120, 167, nil, 167, 167, nil, 167, 167,
- 174, 167, nil, 167, 192, 167, nil, 167, 174, nil,
- 174, 174, nil, 174, 174, 192, 174, nil, 174, 109,
- 174, nil, 174, 192, nil, 192, 192, nil, 192, 192,
- 109, 192, nil, 192, 182, 192, nil, 192, 109, nil,
- 109, 109, nil, 109, 109, 182, 109, nil, 109, nil,
- 109, nil, 109, 182, nil, 182, 182, nil, 182, 182,
- 84, 182, nil, 182, nil, 182, nil, 182, 84, nil,
- 84, 84, nil, 84, 84, 190, 84, nil, 84, nil,
- 84, nil, 84, 190, nil, 190, 190, nil, 190, 190,
- 5, 190, nil, 190, nil, 190, nil, 190, 5, nil,
- 5, 5, nil, 5, 5, 0, 5, nil, 5, nil,
- 5, nil, 5, 0, nil, 0, 0, nil, 0, 0,
- nil, 0, nil, 0, nil, 0, nil, 0 ]
+ 75, 75, 75, 46, 46, 44, 56, 56, 56, 2,
+ 108, 17, 153, 153, 153, 17, 75, 187, 146, 146,
+ 146, 123, 56, 108, 59, 59, 59, 59, 153, 2,
+ 44, 75, 6, 46, 146, 75, 75, 56, 46, 75,
+ 59, 56, 56, 153, 124, 56, 89, 153, 153, 146,
+ 125, 153, 72, 146, 146, 59, 6, 146, 128, 59,
+ 59, 99, 99, 59, 30, 30, 30, 120, 120, 74,
+ 48, 48, 48, 67, 89, 89, 14, 14, 14, 69,
+ 30, 98, 16, 16, 16, 68, 48, 76, 202, 202,
+ 202, 99, 14, 67, 98, 30, 99, 120, 16, 30,
+ 30, 48, 120, 30, 202, 48, 48, 14, 77, 48,
+ 66, 14, 14, 16, 65, 14, 93, 16, 16, 202,
+ 133, 16, 136, 202, 202, 93, 165, 202, 212, 212,
+ 212, 63, 215, 136, 216, 216, 216, 215, 136, 165,
+ 147, 147, 147, 61, 212, 133, 133, 167, 167, 148,
+ 216, 167, 127, 210, 210, 130, 147, 127, 130, 212,
+ 97, 175, 167, 212, 212, 216, 149, 212, 210, 216,
+ 216, 147, 9, 216, 175, 147, 147, 167, 150, 147,
+ 151, 167, 167, 210, 57, 167, 169, 210, 210, 223,
+ 223, 210, 23, 169, 97, 122, 122, 97, 158, 23,
+ 5, 163, 119, 119, 223, 119, 9, 5, 47, 9,
+ 122, 139, 139, 138, 138, 20, 20, 119, 113, 223,
+ 164, 164, 109, 223, 223, 122, 139, 223, 138, 122,
+ 122, 107, 119, 122, 118, 164, 119, 119, 118, 118,
+ 119, 139, 106, 138, 172, 139, 139, 138, 138, 139,
+ 164, 138, 173, 87, 164, 164, 39, 38, 164, 12,
+ 12, 12, 174, 12, 87, 182, 95, 39, 38, 87,
+ 95, 95, 39, 38, 104, 204, 182, 204, 204, 12,
+ 177, 36, 166, 180, 33, 185, 182, 71, 182, 182,
+ 12, 182, 182, 166, 182, 12, 182, 188, 182, 178,
+ 182, 189, 190, 166, 32, 166, 166, 200, 166, 166,
+ 178, 166, 27, 166, 101, 166, 194, 166, 206, 209,
+ 178, 24, 178, 178, 100, 178, 178, 194, 178, 80,
+ 178, 82, 178, 218, 178, 19, 96, 194, 225, 194,
+ 194, 84, 194, 194, 218, 194, 86, 194, 1, 194,
+ 227, 194, 115, nil, 218, nil, 218, 218, nil, 218,
+ 218, 227, 218, nil, 218, nil, 218, 141, 218, nil,
+ nil, 227, nil, 227, 227, nil, 227, 227, 141, 227,
+ nil, 227, nil, 227, 236, 227, nil, nil, 141, nil,
+ 141, 141, nil, 141, 141, 236, 141, nil, 141, nil,
+ 141, 132, 141, nil, nil, 236, nil, 236, 236, nil,
+ 236, 236, 132, 236, nil, 236, nil, 236, 237, 236,
+ nil, nil, 132, nil, 132, 132, nil, 132, 132, 237,
+ 132, nil, 132, nil, 132, nil, 132, nil, nil, 237,
+ nil, 237, 237, nil, 237, 237, 22, 237, nil, 237,
+ nil, 237, nil, 237, nil, nil, 22, nil, 22, 22,
+ nil, 22, 22, 234, 22, nil, 22, nil, 22, nil,
+ 22, nil, nil, 234, nil, 234, 234, nil, 234, 234,
+ 121, 234, nil, 234, nil, 234, nil, 234, nil, nil,
+ 121, nil, 121, 121, nil, 121, 121, 0, 121, nil,
+ 121, nil, 121, nil, 121, nil, nil, 0, nil, 0,
+ 0, nil, 0, 0, nil, 0, nil, 0, nil, 0,
+ nil, 0 ]
racc_action_pointer = [
- 457, nil, 203, 32, nil, 442, 196, nil, nil, 100,
- nil, nil, 98, nil, 104, nil, 14, 3, nil, 158,
- nil, -13, nil, nil, 169, nil, 145, nil, 176, nil,
- 9, nil, nil, nil, nil, 135, nil, nil, nil, 0,
- nil, nil, nil, nil, nil, 23, 146, nil, -2, nil,
- 142, 134, 142, 127, nil, nil, nil, 13, nil, 107,
- 102, 100, 105, nil, 160, 40, 112, nil, 132, 48,
- 122, nil, 132, nil, 174, 34, 190, 177, nil, 17,
- 65, nil, nil, 100, 412, 94, 4, 204, 194, nil,
- 113, nil, 55, 56, nil, 152, nil, nil, 5, 12,
- 28, 24, nil, 61, nil, 24, nil, 75, nil, 382,
- nil, 85, 58, 110, nil, nil, nil, nil, nil, 54,
- 322, 59, 119, nil, nil, 210, nil, nil, 118, nil,
- nil, 170, nil, -6, nil, 135, 108, 10, 197, 277,
- 133, 67, 146, nil, 147, nil, 292, 262, nil, nil,
- nil, nil, nil, 140, nil, nil, 108, nil, 154, nil,
- nil, 164, 216, nil, 165, nil, nil, 337, nil, nil,
- -6, nil, 60, 148, 352, nil, nil, nil, 186, nil,
- 250, nil, 397, nil, nil, nil, nil, nil, nil, nil,
- 427, nil, 367, nil, 307, nil, nil ]
+ 479, 335, -9, nil, nil, 162, 13, nil, nil, 168,
+ nil, nil, 257, nil, 74, nil, 80, 9, nil, 335,
+ 212, nil, 428, 154, 279, nil, nil, 306, nil, nil,
+ 62, nil, 298, 278, nil, nil, 246, nil, 235, 234,
+ nil, nil, nil, nil, -5, nil, 0, 198, 68, nil,
+ nil, nil, nil, nil, nil, nil, 4, 163, nil, 22,
+ nil, 122, nil, 110, nil, 93, 89, 71, 64, 77,
+ nil, 266, 46, nil, 63, -2, 49, 108, nil, nil,
+ 307, nil, 293, nil, 303, nil, 340, 231, nil, 26,
+ nil, nil, nil, 101, nil, 222, 330, 156, 71, 58,
+ 319, 293, nil, nil, 253, nil, 221, 210, 1, 183,
+ nil, nil, nil, 208, nil, 335, nil, nil, 229, 199,
+ 64, 462, 192, 16, 35, 40, nil, 119, 52, nil,
+ 148, nil, 394, 97, nil, nil, 100, nil, 210, 208,
+ nil, 360, nil, nil, nil, nil, 16, 138, 142, 159,
+ 140, 142, nil, 10, nil, nil, nil, nil, 189, nil,
+ nil, nil, nil, 179, 217, 116, 275, 144, nil, 176,
+ nil, nil, 237, 239, 239, 151, nil, 267, 292, nil,
+ 245, nil, 258, nil, nil, 264, nil, -4, 276, 280,
+ 281, nil, nil, nil, 309, nil, nil, nil, nil, nil,
+ 297, nil, 86, nil, 268, nil, 289, nil, nil, 313,
+ 150, nil, 126, nil, nil, 99, 132, nil, 326, nil,
+ nil, nil, nil, 186, nil, 332, nil, 343, nil, nil,
+ nil, nil, nil, nil, 445, nil, 377, 411, nil, nil ]
racc_action_default = [
- -98, -12, -118, -118, -13, -1, -118, -14, -2, -33,
- -15, -3, -118, -5, -118, -6, -118, -118, -7, -118,
- -34, -118, -8, -9, -118, -10, -118, -11, -118, -95,
- -98, -96, -93, -97, -4, -42, -58, -33, -59, -17,
- -18, -20, -21, -22, -110, -118, -51, -55, -118, -60,
- -56, -50, -118, -33, -52, -85, -54, -49, -65, -53,
- -118, -48, -118, -86, -42, -118, -98, -26, -118, -118,
- -98, 197, -118, -108, -118, -118, -116, -118, -43, -118,
- -118, -113, -46, -118, -118, -118, -118, -118, -118, -84,
- -116, -83, -37, -33, -29, -118, -38, -40, -36, -39,
- -35, -31, -27, -118, -99, -98, -41, -118, -109, -118,
- -94, -118, -117, -118, -19, -16, -112, -114, -115, -118,
- -118, -118, -118, -80, -79, -118, -82, -81, -118, -73,
- -74, -118, -67, -118, -71, -118, -42, -32, -118, -118,
- -118, -118, -105, -102, -107, -111, -118, -118, -92, -25,
- -44, -45, -47, -63, -57, -61, -118, -76, -118, -68,
- -66, -118, -118, -24, -116, -30, -23, -118, -88, -100,
- -118, -101, -118, -118, -118, -90, -91, -62, -118, -75,
- -118, -78, -118, -72, -28, -87, -103, -104, -106, -89,
- -118, -77, -118, -70, -118, -69, -64 ]
+ -126, -146, -146, -16, -5, -146, -146, -6, -7, -146,
+ -8, -9, -33, -10, -146, -11, -146, -32, -12, -146,
+ -146, -13, -1, -146, -29, -14, -2, -146, -15, -3,
+ -146, -28, -146, -146, -120, -119, -126, -31, -126, -126,
+ -121, -124, -123, -125, -126, -139, -146, -18, -146, -19,
+ -83, -21, -84, -22, -52, -23, -61, -75, -77, -146,
+ -79, -146, -91, -74, -85, -78, -73, -52, -76, -53,
+ -111, -81, -146, -80, -146, -146, -61, -146, -112, -4,
+ -126, -30, -61, -60, -146, -137, -146, -126, -46, -146,
+ -45, -37, -39, -146, -36, -146, -146, -146, -146, -146,
+ -146, -56, -57, -59, -55, -62, -58, -54, -52, -50,
+ -110, -53, -109, -144, -48, -146, -65, -141, -146, -146,
+ -146, -146, -146, -146, -146, -144, 240, -127, -146, -128,
+ -146, -138, -146, -146, -42, -41, -146, -35, -146, -146,
+ -34, -146, -122, -17, -20, -86, -146, -51, -146, -146,
+ -145, -61, -140, -146, -142, -143, -106, -105, -146, -108,
+ -99, -107, -100, -52, -146, -146, -146, -146, -93, -146,
+ -97, -87, -146, -136, -146, -146, -131, -134, -146, -27,
+ -146, -116, -146, -40, -38, -69, -71, -68, -72, -67,
+ -70, -43, -44, -118, -146, -64, -49, -24, -25, -63,
+ -144, -66, -146, -102, -146, -82, -89, -94, -92, -146,
+ -146, -26, -146, -129, -130, -146, -146, -114, -146, -115,
+ -117, -47, -104, -146, -101, -146, -88, -146, -98, -135,
+ -132, -133, -113, -103, -146, -96, -146, -146, -95, -90 ]
racc_goto_table = [
- 5, 76, 34, 43, 32, 8, 58, 42, 62, 111,
- 63, 132, 39, 143, 150, 51, 52, 51, 130, 94,
- 74, 128, 134, 135, 177, 96, 89, 119, 97, 131,
- 90, 83, 138, 129, 101, 156, 103, 114, 30, 43,
- 67, 141, 82, 42, 2, nil, nil, nil, 80, 89,
- 89, 51, nil, nil, 159, nil, 157, nil, 100, 128,
- 107, nil, 99, 106, nil, 134, nil, 92, nil, nil,
- nil, 104, 51, 43, nil, nil, 110, 42, 186, 43,
- 122, 127, 127, 42, 120, 126, 126, 89, 121, 51,
- 123, 123, 165, 89, nil, nil, 183, 184, 96, 89,
- 180, 97, 164, nil, nil, nil, nil, 151, nil, 147,
- 140, 191, nil, 152, 128, nil, 51, 34, nil, 127,
- nil, nil, 51, 126, 89, 127, nil, nil, 123, 126,
- nil, 100, nil, 41, 123, 99, 57, nil, 57, 167,
- 92, nil, 89, nil, 34, 68, 174, nil, nil, nil,
- nil, nil, 181, nil, nil, nil, 127, nil, nil, nil,
- 126, 51, nil, nil, 34, 123, 187, 188, nil, 41,
- nil, 34, 57, 91, 127, 51, 51, nil, 126, nil,
- nil, nil, 192, 123, nil, nil, nil, nil, 98, 34,
- 194, 34, nil, 57, nil, nil, 91, 91, nil, nil,
- nil, nil, nil, 41, nil, nil, nil, nil, nil, 41,
- 57, 124, 124, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 142,
- nil, nil, nil, nil, 91, nil, nil, 57, nil, nil,
- 91, nil, nil, 57, nil, nil, 91, nil, nil, 124,
- nil, nil, nil, nil, nil, 124, nil, nil, nil, nil,
- nil, 98, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 91, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 57, nil, nil, nil, 124, nil, nil, 91,
- nil, nil, nil, nil, 142, nil, 57, 57, nil, nil,
- nil, nil, nil, nil, 124 ]
+ 22, 79, 51, 162, 102, 47, 113, 55, 62, 40,
+ 74, 114, 103, 153, 158, 78, 176, 170, 109, 149,
+ 168, 89, 95, 72, 83, 199, 125, 36, 88, 88,
+ 86, 172, 130, 191, 192, 226, 51, 167, 96, 98,
+ 26, 55, 100, 148, 144, 80, 104, 73, 203, 73,
+ 160, 107, 31, 116, 204, 199, 53, 118, 128, 158,
+ 37, 44, 170, 73, 184, 207, 174, 175, 19, 123,
+ 133, nil, nil, nil, nil, nil, 85, 88, nil, nil,
+ nil, 73, 90, 90, 85, nil, 186, 186, nil, 51,
+ 53, nil, 73, nil, 55, 102, nil, 142, nil, 223,
+ 106, 200, 196, 103, 230, 228, 221, 233, 73, 157,
+ 51, 112, 157, 165, 161, 55, nil, 161, 158, nil,
+ 129, 166, nil, nil, nil, nil, 88, nil, 187, 187,
+ nil, 90, 182, 189, 189, nil, nil, 104, nil, nil,
+ 195, 194, 107, 53, nil, 79, nil, 201, nil, nil,
+ nil, nil, nil, nil, 157, nil, nil, 157, nil, 161,
+ 110, 79, 161, 159, 53, 101, 159, nil, nil, nil,
+ nil, nil, nil, 79, 112, nil, nil, 112, 218, 73,
+ 90, nil, 188, 188, nil, nil, 73, nil, nil, nil,
+ nil, 106, nil, 112, 112, nil, 222, 79, nil, nil,
+ 157, nil, 112, nil, nil, 161, 229, nil, 159, nil,
+ 231, 159, nil, 157, nil, 79, 79, nil, 161, 112,
+ nil, nil, 112, 110, nil, nil, 110, 236, 156, nil,
+ nil, 156, nil, nil, 237, 73, nil, nil, nil, nil,
+ nil, nil, 190, 190, nil, 73, nil, 185, 185, 73,
+ nil, 110, nil, nil, 159, nil, 101, nil, nil, nil,
+ nil, nil, nil, nil, nil, 112, nil, 159, 110, nil,
+ nil, 110, nil, 156, nil, nil, 156, nil, 112, 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, nil, nil, nil,
+ nil, nil, nil, nil, 110, nil, nil, nil, nil, 156,
+ nil, nil, nil, nil, nil, nil, nil, 110, nil, nil,
+ nil, nil, 156 ]
racc_goto_check = [
- 2, 23, 4, 20, 49, 3, 30, 19, 30, 24,
- 20, 40, 16, 51, 31, 25, 37, 25, 44, 26,
- 47, 42, 42, 24, 38, 28, 34, 33, 29, 39,
- 23, 32, 22, 43, 21, 45, 46, 17, 48, 20,
- 5, 50, 30, 19, 1, nil, nil, nil, 16, 34,
- 34, 25, nil, nil, 40, nil, 44, nil, 20, 42,
- 47, nil, 19, 30, nil, 42, nil, 25, nil, nil,
- nil, 3, 25, 20, nil, nil, 49, 19, 51, 20,
- 30, 20, 20, 19, 2, 19, 19, 34, 16, 25,
- 25, 25, 26, 34, nil, nil, 42, 24, 28, 34,
- 33, 29, 23, nil, nil, nil, nil, 30, nil, 2,
- 3, 44, nil, 30, 42, nil, 25, 4, nil, 20,
- nil, nil, 25, 19, 34, 20, nil, nil, 25, 19,
- nil, 20, nil, 18, 25, 19, 18, nil, 18, 2,
- 25, nil, 34, nil, 4, 18, 2, nil, nil, nil,
- nil, nil, 30, nil, nil, nil, 20, nil, nil, nil,
- 19, 25, nil, nil, 4, 25, 30, 30, nil, 18,
- nil, 4, 18, 36, 20, 25, 25, nil, 19, nil,
- nil, nil, 2, 25, nil, nil, nil, nil, 18, 4,
- 2, 4, nil, 18, nil, nil, 36, 36, nil, nil,
- nil, nil, nil, 18, nil, nil, nil, nil, nil, 18,
- 18, 18, 18, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 18,
- nil, nil, nil, nil, 36, nil, nil, 18, nil, nil,
- 36, nil, nil, 18, nil, nil, 36, nil, nil, 18,
- nil, nil, nil, nil, nil, 18, nil, nil, nil, nil,
- nil, 18, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 36, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 18, nil, nil, nil, 18, nil, nil, 36,
- nil, nil, nil, nil, 18, nil, 18, 18, nil, nil,
- nil, nil, nil, nil, 18 ]
+ 2, 4, 19, 54, 39, 17, 24, 21, 41, 60,
+ 41, 36, 40, 44, 52, 21, 63, 52, 22, 25,
+ 50, 30, 30, 47, 41, 42, 24, 56, 19, 19,
+ 58, 25, 24, 35, 35, 48, 19, 49, 58, 17,
+ 3, 21, 41, 23, 18, 56, 19, 26, 54, 26,
+ 53, 21, 5, 41, 55, 42, 20, 43, 57, 52,
+ 29, 59, 52, 26, 31, 50, 61, 62, 1, 41,
+ 30, nil, nil, nil, nil, nil, 3, 19, nil, nil,
+ nil, 26, 20, 20, 3, nil, 39, 39, nil, 19,
+ 20, nil, 26, nil, 21, 39, nil, 60, nil, 44,
+ 20, 24, 36, 40, 63, 52, 25, 54, 26, 19,
+ 19, 46, 19, 17, 21, 21, nil, 21, 52, nil,
+ 3, 2, nil, nil, nil, nil, 19, nil, 19, 19,
+ nil, 20, 2, 21, 21, nil, nil, 19, nil, nil,
+ 41, 2, 21, 20, nil, 4, nil, 41, nil, nil,
+ nil, nil, nil, nil, 19, nil, nil, 19, nil, 21,
+ 45, 4, 21, 20, 20, 38, 20, nil, nil, nil,
+ nil, nil, nil, 4, 46, nil, nil, 46, 2, 26,
+ 20, nil, 20, 20, nil, nil, 26, nil, nil, nil,
+ nil, 20, nil, 46, 46, nil, 41, 4, nil, nil,
+ 19, nil, 46, nil, nil, 21, 41, nil, 20, nil,
+ 41, 20, nil, 19, nil, 4, 4, nil, 21, 46,
+ nil, nil, 46, 45, nil, nil, 45, 2, 38, nil,
+ nil, 38, nil, nil, 2, 26, nil, nil, nil, nil,
+ nil, nil, 45, 45, nil, 26, nil, 38, 38, 26,
+ nil, 45, nil, nil, 20, nil, 38, nil, nil, nil,
+ nil, nil, nil, nil, nil, 46, nil, 20, 45, nil,
+ nil, 45, nil, 38, nil, nil, 38, nil, 46, 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, nil, nil, nil,
+ nil, nil, nil, nil, 45, nil, nil, nil, nil, 38,
+ nil, nil, nil, nil, nil, nil, nil, 45, nil, nil,
+ nil, nil, 38 ]
racc_goto_pointer = [
- nil, 44, 0, 5, -3, 19, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 3, -42, 124, -2,
- -6, -30, -69, -34, -67, 3, -45, nil, -39, -36,
- -6, -98, -17, -56, -38, nil, 109, 4, -129, -59,
- -77, nil, -66, -54, -69, -90, -30, -10, 35, 1,
- -64, -92, nil ]
+ nil, 68, 0, 40, -21, 50, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, -7, -55, -10,
+ 44, -5, -38, -66, -50, -94, 33, nil, nil, 54,
+ -17, -72, nil, nil, nil, -105, -45, nil, 109, -52,
+ -44, -6, -125, -2, -105, 104, 55, 9, -171, -85,
+ -102, nil, -105, -69, -116, -110, 22, -22, -6, 52,
+ 0, -61, -60, -111 ]
racc_goto_default = [
- nil, nil, nil, 73, 11, 13, 15, 18, 22, 23,
- 25, 27, 1, 4, 7, 10, nil, 40, 17, 59,
- 61, nil, nil, nil, nil, 6, nil, 95, 54, 56,
- nil, 78, nil, nil, 46, 47, 50, nil, nil, nil,
- nil, 133, 60, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, 144 ]
+ nil, nil, nil, 94, 29, 4, 7, 8, 10, 11,
+ 13, 15, 18, 21, 25, 28, 3, nil, 49, 63,
+ 65, 66, nil, nil, nil, nil, 27, 2, 6, nil,
+ nil, 91, 136, 92, 93, nil, nil, 115, 57, 58,
+ 60, nil, 105, nil, nil, 68, 71, nil, nil, nil,
+ nil, 169, 61, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil ]
racc_token_table = {
false => 0,
@@ -550,34 +627,41 @@ racc_token_table = {
:DOT => 16,
:COLON => 17,
:TYPE => 18,
- :QMARK => 19,
- :LPAREN => 20,
- :RPAREN => 21,
- :ISEQUAL => 22,
- :GREATEREQUAL => 23,
- :GREATERTHAN => 24,
- :LESSTHAN => 25,
- :IF => 26,
- :ELSE => 27,
- :IMPORT => 28,
- :DEFINE => 29,
- :ELSIF => 30,
- :VARIABLE => 31,
- :CLASS => 32,
- :INHERITS => 33,
- :NODE => 34,
- :BOOLEAN => 35,
- :NAME => 36,
- :SEMIC => 37,
- :CASE => 38,
- :DEFAULT => 39,
- :AT => 40,
- :LCOLLECT => 41,
- :RCOLLECT => 42 }
+ :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 }
racc_use_result_var = true
-racc_nt_base = 43
+racc_nt_base = 50
Racc_arg = [
racc_action_table,
@@ -615,6 +699,8 @@ Racc_token_to_s_table = [
'DOT',
'COLON',
'TYPE',
+'LLCOLLECT',
+'RRCOLLECT',
'QMARK',
'LPAREN',
'RPAREN',
@@ -639,13 +725,18 @@ Racc_token_to_s_table = [
'AT',
'LCOLLECT',
'RCOLLECT',
+'CLASSNAME',
+'CLASSREF',
+'NOT',
+'OR',
+'AND',
'$start',
'program',
'statements',
-'nothing',
+'nil',
'statement',
-'object',
-'collectable',
+'resource',
+'virtualresource',
'collection',
'assignment',
'casestatement',
@@ -655,18 +746,29 @@ Racc_token_to_s_table = [
'definition',
'hostclass',
'nodedef',
-'classnames',
-'classname',
+'resourceoverride',
+'namestrings',
+'namestring',
'name',
'variable',
'quotedtext',
-'objectinstances',
+'resourceinstances',
'endsemi',
'params',
'endcomma',
+'resourceref',
+'at',
+'collectname',
+'collectrhand',
+'collstatements',
+'collstatement',
+'colljoin',
+'collexpr',
+'colllval',
+'simplervalue',
+'resourceinst',
+'resourcename',
'type',
-'objectinst',
-'objectname',
'selector',
'array',
'rvalue',
@@ -674,7 +776,6 @@ Racc_token_to_s_table = [
'rvalues',
'comma',
'boolean',
-'objectref',
'funcrvalue',
'iftest',
'else',
@@ -685,13 +786,14 @@ Racc_token_to_s_table = [
'svalues',
'selectval',
'sintvalues',
+'fqname',
'argumentlist',
'parent',
'hostnames',
'hostname',
+'nothing',
'arguments',
-'argument',
-'lvariable']
+'argument']
Racc_debug_parser = false
@@ -699,13 +801,21 @@ Racc_debug_parser = false
# reduce 0 omitted
-module_eval <<'.,.,', 'grammar.ra', 24
+module_eval <<'.,.,', 'grammar.ra', 33
def _reduce_1( val, _values, result )
- # Make sure we always return an array.
- if val[0].is_a?(AST::ASTArray)
- result = val[0]
+ 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 = aryfy(val[0])
+ result = nil
end
result
end
@@ -715,13 +825,18 @@ module_eval <<'.,.,', 'grammar.ra', 24
# reduce 3 omitted
-module_eval <<'.,.,', 'grammar.ra', 35
+module_eval <<'.,.,', 'grammar.ra', 49
def _reduce_4( val, _values, result )
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[1])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[1]]
+ 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
@@ -749,8 +864,10 @@ module_eval <<'.,.,', 'grammar.ra', 35
# reduce 15 omitted
-module_eval <<'.,.,', 'grammar.ra', 56
- def _reduce_16( val, _values, result )
+ # reduce 16 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 71
+ def _reduce_17( val, _values, result )
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
@@ -760,8 +877,8 @@ module_eval <<'.,.,', 'grammar.ra', 56
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 63
- def _reduce_17( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 78
+ def _reduce_18( val, _values, result )
args = aryfy(val[1])
result = ast AST::Function,
:name => val[0],
@@ -771,10 +888,10 @@ module_eval <<'.,.,', 'grammar.ra', 63
end
.,.,
- # reduce 18 omitted
+ # reduce 19 omitted
-module_eval <<'.,.,', 'grammar.ra', 82
- def _reduce_19( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 85
+ def _reduce_20( val, _values, result )
result = aryfy(val[0], val[2])
result.line = @lexer.line
result.file = @lexer.file
@@ -782,80 +899,86 @@ module_eval <<'.,.,', 'grammar.ra', 82
end
.,.,
- # reduce 20 omitted
-
# reduce 21 omitted
# reduce 22 omitted
-module_eval <<'.,.,', 'grammar.ra', 111
- def _reduce_23( val, _values, result )
+ # reduce 23 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 113
+ def _reduce_24( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid name"
+ error "Invalid name"
end
array = val[2]
- if array.instance_of?(AST::ObjectInst)
+ if array.instance_of?(AST::ResourceInst)
array = [array]
end
result = ast AST::ASTArray
- # this iterates across each specified objectinstance
+ # this iterates across each specified resourceinstance
array.each { |instance|
- unless instance.instance_of?(AST::ObjectInst)
+ unless instance.instance_of?(AST::ResourceInst)
raise Puppet::Dev, "Got something that isn't an instance"
end
# now, i need to somehow differentiate between those things with
# arrays in their names, and normal things
- result.push ast(AST::ObjectDef,
+ result.push ast(AST::ResourceDef,
:type => val[0],
- :name => instance[0],
+ :title => instance[0],
:params => instance[1])
}
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 120
- def _reduce_24( val, _values, result )
- if val[0].instance_of?(AST::ASTArray)
- Puppet.notice "invalid name"
- raise Puppet::ParseError, "Invalid name"
- end
- # an object but without a name
- # this cannot be an instance of a library type
- result = ast AST::ObjectDef, :type => val[0], :params => val[2]
+module_eval <<'.,.,', 'grammar.ra', 116
+ def _reduce_25( val, _values, result )
+ # This is a deprecated syntax.
+ error "All resource specifications require names"
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 126
- def _reduce_25( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 122
+ def _reduce_26( val, _values, result )
# a template setting for a type
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid type"
+ error "Invalid type"
end
- result = ast(AST::TypeDefaults, :type => val[0], :params => val[2])
+ result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2])
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 149
- def _reduce_26( val, _values, result )
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+module_eval <<'.,.,', 'grammar.ra', 127
+ def _reduce_27( val, _values, result )
+ result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 154
+ def _reduce_28( val, _values, result )
+ type = val[0]
+
+ if type == :exported and ! Puppet[:storeconfigs]
+ error "You cannot collect without storeconfigs being set"
end
- if val[1].is_a? AST::TypeDefaults
- raise Puppet::ParseError, "Defaults are not collectable"
+ if val[1].is_a? AST::ResourceDefaults
+ error "Defaults are not virtualizable"
end
- # Just mark our objects as collectable and pass them through.
+ 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.collectable = true
+ obj.send(method, true)
end
else
- val[1].collectable = true
+ val[1].send(method, true)
end
result = val[1]
@@ -863,28 +986,130 @@ module_eval <<'.,.,', 'grammar.ra', 149
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 158
- def _reduce_27( val, _values, result )
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+module_eval <<'.,.,', 'grammar.ra', 155
+ def _reduce_29( val, _values, result )
+ result = :virtual
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 156
+ def _reduce_30( val, _values, result )
+ result = :exported
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 179
+ def _reduce_31( 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]
+ error "You cannot collect exported resources without storeconfigs being set"
end
- result = ast AST::Collection, :type => val[0]
+ result = ast AST::Collection, args
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 162
- def _reduce_28( val, _values, result )
- result = ast AST::ObjectInst, :children => [val[0],val[2]]
+ # reduce 32 omitted
+
+ # reduce 33 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 190
+ def _reduce_34( val, _values, result )
+ if val[1]
+ result = val[1]
+ result.form = :virtual
+ else
+ result = :virtual
+ end
result
end
.,.,
- # reduce 29 omitted
+module_eval <<'.,.,', 'grammar.ra', 198
+ def _reduce_35( val, _values, result )
+ if val[1]
+ result = val[1]
+ result.form = :exported
+ else
+ result = :exported
+ end
+ result
+ end
+.,.,
-module_eval <<'.,.,', 'grammar.ra', 172
- def _reduce_30( val, _values, result )
- if val[0].instance_of?(AST::ObjectInst)
+ # reduce 36 omitted
+
+ # reduce 37 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 206
+ def _reduce_38( val, _values, result )
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ result
+ end
+.,.,
+
+ # reduce 39 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 212
+ def _reduce_40( val, _values, result )
+ result = val[1]
+ result.parens = true
+ result
+ end
+.,.,
+
+ # reduce 41 omitted
+
+ # reduce 42 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 220
+ def _reduce_43( val, _values, result )
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 225
+ def _reduce_44( val, _values, result )
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+ result
+ end
+.,.,
+
+ # reduce 45 omitted
+
+ # reduce 46 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 232
+ def _reduce_47( val, _values, result )
+ result = ast AST::ResourceInst, :children => [val[0],val[2]]
+ result
+ end
+.,.,
+
+ # reduce 48 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 242
+ def _reduce_49( val, _values, result )
+ if val[0].instance_of?(AST::ResourceInst)
result = ast AST::ASTArray, :children => [val[0],val[2]]
else
val[0].push val[2]
@@ -894,61 +1119,61 @@ module_eval <<'.,.,', 'grammar.ra', 172
end
.,.,
- # reduce 31 omitted
+ # reduce 50 omitted
- # reduce 32 omitted
+ # reduce 51 omitted
-module_eval <<'.,.,', 'grammar.ra', 179
- def _reduce_33( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 249
+ def _reduce_52( val, _values, result )
result = ast AST::Name, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 183
- def _reduce_34( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 253
+ def _reduce_53( val, _values, result )
result = ast AST::Type, :value => val[0]
result
end
.,.,
- # reduce 35 omitted
+ # reduce 54 omitted
- # reduce 36 omitted
+ # reduce 55 omitted
- # reduce 37 omitted
+ # reduce 56 omitted
- # reduce 38 omitted
+ # reduce 57 omitted
- # reduce 39 omitted
+ # reduce 58 omitted
- # reduce 40 omitted
+ # reduce 59 omitted
-module_eval <<'.,.,', 'grammar.ra', 196
- def _reduce_41( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 266
+ def _reduce_60( val, _values, result )
# this is distinct from referencing a variable
- variable = ast AST::Name, :value => val[0].sub(/^\$/,'')
+ variable = ast AST::Name, :value => val[0]
result = ast AST::VarDef, :name => variable, :value => val[2]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 201
- def _reduce_42( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 271
+ def _reduce_61( val, _values, result )
result = ast AST::ASTArray
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 201
- def _reduce_43( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 271
+ def _reduce_62( val, _values, result )
result = val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 210
- def _reduce_44( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 280
+ def _reduce_63( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
result = val[0]
@@ -959,18 +1184,17 @@ module_eval <<'.,.,', 'grammar.ra', 210
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 215
- def _reduce_45( val, _values, result )
- leaf = ast AST::String, :value => val[0]
- result = ast AST::ObjectParam, :param => leaf, :value => val[2]
+module_eval <<'.,.,', 'grammar.ra', 284
+ def _reduce_64( val, _values, result )
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
result
end
.,.,
- # reduce 46 omitted
+ # reduce 65 omitted
-module_eval <<'.,.,', 'grammar.ra', 224
- def _reduce_47( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 293
+ def _reduce_66( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
result = val[0].push(val[2])
else
@@ -980,26 +1204,38 @@ module_eval <<'.,.,', 'grammar.ra', 224
end
.,.,
- # reduce 48 omitted
+ # reduce 67 omitted
- # reduce 49 omitted
+ # reduce 68 omitted
- # reduce 50 omitted
+ # reduce 69 omitted
- # reduce 51 omitted
+ # reduce 70 omitted
- # reduce 52 omitted
+ # reduce 71 omitted
- # reduce 53 omitted
+ # reduce 72 omitted
- # reduce 54 omitted
+ # reduce 73 omitted
- # reduce 55 omitted
+ # reduce 74 omitted
- # reduce 56 omitted
+ # reduce 75 omitted
+
+ # reduce 76 omitted
+
+ # reduce 77 omitted
+
+ # reduce 78 omitted
+
+ # reduce 79 omitted
-module_eval <<'.,.,', 'grammar.ra', 243
- def _reduce_57( val, _values, result )
+ # reduce 80 omitted
+
+ # reduce 81 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 319
+ def _reduce_82( val, _values, result )
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
@@ -1009,36 +1245,44 @@ module_eval <<'.,.,', 'grammar.ra', 243
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 247
- def _reduce_58( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 323
+ def _reduce_83( val, _values, result )
result = ast AST::String, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 249
- def _reduce_59( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 325
+ def _reduce_84( val, _values, result )
result = ast AST::FlatString, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 253
- def _reduce_60( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 329
+ def _reduce_85( val, _values, result )
result = ast AST::Boolean, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 257
- def _reduce_61( val, _values, result )
- result = ast AST::ObjectRef, :type => val[0], :name => val[2]
+module_eval <<'.,.,', 'grammar.ra', 334
+ def _reduce_86( val, _values, result )
+ Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 270
- def _reduce_62( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 336
+ def _reduce_87( val, _values, result )
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 349
+ def _reduce_88( val, _values, result )
args = {
:test => val[1],
:statements => val[3]
@@ -1053,19 +1297,19 @@ module_eval <<'.,.,', 'grammar.ra', 270
end
.,.,
- # reduce 63 omitted
+ # reduce 89 omitted
-module_eval <<'.,.,', 'grammar.ra', 275
- def _reduce_64( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 354
+ def _reduce_90( val, _values, result )
result = ast AST::Else, :statements => val[2]
result
end
.,.,
- # reduce 65 omitted
+ # reduce 91 omitted
-module_eval <<'.,.,', 'grammar.ra', 287
- def _reduce_66( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 366
+ def _reduce_92( val, _values, result )
options = val[3]
unless options.instance_of?(AST::ASTArray)
options = ast AST::ASTArray, :children => [val[3]]
@@ -1075,10 +1319,10 @@ module_eval <<'.,.,', 'grammar.ra', 287
end
.,.,
- # reduce 67 omitted
+ # reduce 93 omitted
-module_eval <<'.,.,', 'grammar.ra', 297
- def _reduce_68( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 376
+ def _reduce_94( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push val[1]
result = val[0]
@@ -1089,15 +1333,15 @@ module_eval <<'.,.,', 'grammar.ra', 297
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 301
- def _reduce_69( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 380
+ def _reduce_95( val, _values, result )
result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 306
- def _reduce_70( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 385
+ def _reduce_96( val, _values, result )
result = ast(AST::CaseOpt,
:value => val[0],
:statements => ast(AST::ASTArray)
@@ -1106,10 +1350,10 @@ module_eval <<'.,.,', 'grammar.ra', 306
end
.,.,
- # reduce 71 omitted
+ # reduce 97 omitted
-module_eval <<'.,.,', 'grammar.ra', 316
- def _reduce_72( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 395
+ def _reduce_98( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
result = val[0]
@@ -1120,26 +1364,26 @@ module_eval <<'.,.,', 'grammar.ra', 316
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 320
- def _reduce_73( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 399
+ def _reduce_99( val, _values, result )
result = ast AST::Selector, :param => val[0], :values => val[2]
result
end
.,.,
- # reduce 74 omitted
+ # reduce 100 omitted
-module_eval <<'.,.,', 'grammar.ra', 322
- def _reduce_75( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 401
+ def _reduce_101( val, _values, result )
result = val[1]
result
end
.,.,
- # reduce 76 omitted
+ # reduce 102 omitted
-module_eval <<'.,.,', 'grammar.ra', 333
- def _reduce_77( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 412
+ def _reduce_103( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
result = val[0]
@@ -1150,34 +1394,34 @@ module_eval <<'.,.,', 'grammar.ra', 333
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 337
- def _reduce_78( val, _values, result )
- result = ast AST::ObjectParam, :param => val[0], :value => val[2]
+module_eval <<'.,.,', 'grammar.ra', 416
+ def _reduce_104( val, _values, result )
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
result
end
.,.,
- # reduce 79 omitted
+ # reduce 105 omitted
- # reduce 80 omitted
+ # reduce 106 omitted
- # reduce 81 omitted
+ # reduce 107 omitted
- # reduce 82 omitted
+ # reduce 108 omitted
- # reduce 83 omitted
+ # reduce 109 omitted
- # reduce 84 omitted
+ # reduce 110 omitted
-module_eval <<'.,.,', 'grammar.ra', 347
- def _reduce_85( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 426
+ def _reduce_111( val, _values, result )
result = ast AST::Default, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 404
- def _reduce_86( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 488
+ def _reduce_112( val, _values, result )
# importing files
# yuk, i hate keywords
# we'll probably have to have some kind of search path eventually
@@ -1209,7 +1453,7 @@ module_eval <<'.,.,', 'grammar.ra', 404
end
files.each { |file|
- parser = Puppet::Parser::Parser.new()
+ parser = Puppet::Parser::Parser.new(interp)
parser.files = self.files
Puppet.debug("importing '%s'" % file)
@@ -1227,8 +1471,13 @@ module_eval <<'.,.,', 'grammar.ra', 404
end
# push the results into the main result array
# We always return an array when we parse.
- parser.parse.each do |child|
- result.push child
+ ast = parser.parse
+
+ # Things that just get added to the classtable or whatever return nil
+ if ast
+ ast.each do |child|
+ result.push child
+ end
end
}
}
@@ -1236,249 +1485,173 @@ module_eval <<'.,.,', 'grammar.ra', 404
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 420
- def _reduce_87( val, _values, result )
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => val[4] # Switch to 5 for parents
- }
+module_eval <<'.,.,', 'grammar.ra', 498
+ def _reduce_113( val, _values, result )
+ interp.newdefine fqname(val[1]), :arguments => val[2], :code => val[4]
+ @lexer.indefine = false
+ result = nil
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
- result = ast AST::CompDef, args
#} | DEFINE NAME argumentlist parent LBRACE RBRACE {
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 432
- def _reduce_88( val, _values, result )
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => ast(AST::ASTArray)
- }
-
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
-
- result = ast AST::CompDef, args
+module_eval <<'.,.,', 'grammar.ra', 502
+ def _reduce_114( val, _values, result )
+ interp.newdefine fqname(val[1]), :arguments => val[2]
+ @lexer.indefine = false
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 446
- def _reduce_89( val, _values, result )
- #:args => val[2],
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => val[4]
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
+module_eval <<'.,.,', 'grammar.ra', 510
+ def _reduce_115( val, _values, result )
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :code => val[4], :parent => val[2]
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 456
- def _reduce_90( val, _values, result )
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => ast(AST::ASTArray, :children => [])
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
+module_eval <<'.,.,', 'grammar.ra', 515
+ def _reduce_116( val, _values, result )
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :parent => val[2]
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 473
- def _reduce_91( val, _values, result )
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => val[4]
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef, args
+module_eval <<'.,.,', 'grammar.ra', 520
+ def _reduce_117( val, _values, result )
+ interp.newnode val[1], :parent => val[2], :code => val[4]
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 488
- def _reduce_92( val, _values, result )
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => ast(AST::ASTArray, :children => [])
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef,args
+module_eval <<'.,.,', 'grammar.ra', 523
+ def _reduce_118( val, _values, result )
+ interp.newnode val[1], :parent => val[2]
+ result = nil
result
end
.,.,
- # reduce 93 omitted
+ # reduce 119 omitted
-module_eval <<'.,.,', 'grammar.ra', 499
- def _reduce_94( val, _values, result )
- if val[0].instance_of?(AST::ASTArray)
- result = val[0]
- result.push val[2]
- else
- result = ast AST::ASTArray, :children => [val[0], val[2]]
- end
- result
- end
-.,.,
+ # reduce 120 omitted
-module_eval <<'.,.,', 'grammar.ra', 503
- def _reduce_95( val, _values, result )
- result = ast AST::HostName, :value => val[0]
- result
- end
-.,.,
+ # reduce 121 omitted
-module_eval <<'.,.,', 'grammar.ra', 505
- def _reduce_96( val, _values, result )
- result = ast AST::HostName, :value => val[0]
+module_eval <<'.,.,', 'grammar.ra', 535
+ def _reduce_122( val, _values, result )
+ result = val[0]
+ result = [result] unless result.is_a?(Array)
+ result << val[2]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 507
- def _reduce_97( val, _values, result )
- result = ast AST::Default, :value => val[0]
+ # reduce 123 omitted
+
+ # reduce 124 omitted
+
+ # reduce 125 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 543
+ def _reduce_126( val, _values, result )
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 511
- def _reduce_98( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 547
+ def _reduce_127( val, _values, result )
result = ast AST::ASTArray, :children => []
result
end
.,.,
- # reduce 99 omitted
+ # reduce 128 omitted
-module_eval <<'.,.,', 'grammar.ra', 516
- def _reduce_100( val, _values, result )
- result = val[1]
+module_eval <<'.,.,', 'grammar.ra', 552
+ def _reduce_129( val, _values, result )
+ result = nil
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 523
- def _reduce_101( val, _values, result )
- if val[1].instance_of?(AST::ASTArray)
- result = val[1]
- else
- result = ast AST::ASTArray, :children => [val[1]]
- end
+module_eval <<'.,.,', 'grammar.ra', 556
+ def _reduce_130( val, _values, result )
+ result = val[1]
+ result = [result] unless result[0].is_a?(Array)
result
end
.,.,
- # reduce 102 omitted
+ # reduce 131 omitted
-module_eval <<'.,.,', 'grammar.ra', 533
- def _reduce_103( val, _values, result )
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[2])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[2]]
- end
+module_eval <<'.,.,', 'grammar.ra', 563
+ def _reduce_132( val, _values, result )
+ result = val[0]
+ result = [result] unless result[0].is_a?(Array)
+ result << val[2]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 541
- def _reduce_104( val, _values, result )
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0],val[2]]
+module_eval <<'.,.,', 'grammar.ra', 568
+ def _reduce_133( val, _values, result )
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0], val[2]]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 548
- def _reduce_105( val, _values, result )
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0]]
+module_eval <<'.,.,', 'grammar.ra', 572
+ def _reduce_134( val, _values, result )
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0]]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 550
- def _reduce_106( val, _values, result )
- result = ast AST::CompArgument, :children => [val[0],val[2]]
+module_eval <<'.,.,', 'grammar.ra', 574
+ def _reduce_135( val, _values, result )
+ result = [val[0], val[2]]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 552
- def _reduce_107( val, _values, result )
- result = ast AST::CompArgument, :children => [val[0]]
+module_eval <<'.,.,', 'grammar.ra', 576
+ def _reduce_136( val, _values, result )
+ result = [val[0]]
result
end
.,.,
- # reduce 108 omitted
+ # reduce 137 omitted
-module_eval <<'.,.,', 'grammar.ra', 557
- def _reduce_109( val, _values, result )
- result = ast AST::Name, :value => val[1]
+module_eval <<'.,.,', 'grammar.ra', 581
+ def _reduce_138( val, _values, result )
+ result = val[1]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 562
- def _reduce_110( val, _values, result )
- name = val[0].sub(/^\$/,'')
- result = ast AST::Variable, :value => name
+module_eval <<'.,.,', 'grammar.ra', 585
+ def _reduce_139( val, _values, result )
+ result = ast AST::Variable, :value => val[0]
result
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 567
- def _reduce_111( val, _values, result )
- result = ast AST::Name, :value => val[0].sub(/^\$/,'')
- result
- end
-.,.,
-
-module_eval <<'.,.,', 'grammar.ra', 575
- def _reduce_112( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 593
+ def _reduce_140( val, _values, result )
if val[1].instance_of?(AST::ASTArray)
result = val[1]
else
@@ -1488,21 +1661,21 @@ module_eval <<'.,.,', 'grammar.ra', 575
end
.,.,
-module_eval <<'.,.,', 'grammar.ra', 577
- def _reduce_113( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 595
+ def _reduce_141( val, _values, result )
result = ast AST::ASTArray
result
end
.,.,
- # reduce 114 omitted
+ # reduce 142 omitted
- # reduce 115 omitted
+ # reduce 143 omitted
- # reduce 116 omitted
+ # reduce 144 omitted
-module_eval <<'.,.,', 'grammar.ra', 582
- def _reduce_117( val, _values, result )
+module_eval <<'.,.,', 'grammar.ra', 600
+ def _reduce_145( val, _values, result )
result = nil
result
end
diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb
new file mode 100644
index 000000000..5d09eaa9b
--- /dev/null
+++ b/lib/puppet/parser/resource.rb
@@ -0,0 +1,324 @@
+# A resource that we're managing. This handles making sure that only subclasses
+# can set parameters.
+class Puppet::Parser::Resource
+ require 'puppet/parser/resource/param'
+ require 'puppet/parser/resource/reference'
+ ResParam = Struct.new :name, :value, :source, :line, :file
+ include Puppet::Util
+ include Puppet::Util::MethodHelper
+ include Puppet::Util::Errors
+ include Puppet::Util::Logging
+
+ attr_accessor :source, :line, :file, :scope
+ attr_accessor :virtual, :override, :params, :translated
+
+ attr_reader :exported
+
+ attr_writer :tags
+
+ # Proxy a few methods to our @ref object.
+ [:builtin?, :type, :title].each do |method|
+ define_method(method) do
+ @ref.send(method)
+ end
+ end
+
+ # Set up some boolean test methods
+ [:exported, :translated, :override].each do |method|
+ newmeth = (method.to_s + "?").intern
+ define_method(newmeth) do
+ self.send(method)
+ end
+ end
+
+ def [](param)
+ param = symbolize(param)
+ if param == :title
+ return self.title
+ end
+ if @params.has_key?(param)
+ @params[param].value
+ else
+ nil
+ end
+ end
+
+ # Add default values from our definition.
+ def adddefaults
+ defaults = scope.lookupdefaults(self.type)
+
+ defaults.each do |name, param|
+ unless @params.include?(param.name)
+ self.debug "Adding default for %s" % param.name
+
+ @params[param.name] = param
+ end
+ end
+ end
+
+ # Add any metaparams defined in our scope. This actually adds any metaparams
+ # from any parent scope, and there's currently no way to turn that off.
+ def addmetaparams
+ Puppet::Type.eachmetaparam do |name|
+ if val = scope.lookupvar(name.to_s, false)
+ unless val == :undefined
+ set Param.new(:name => name, :value => val, :source => scope.source)
+ end
+ end
+ end
+ end
+
+ # Add any overrides for this object.
+ def addoverrides
+ overrides = scope.lookupoverrides(self)
+
+ overrides.each do |over|
+ self.merge(over)
+ end
+
+ overrides.clear
+ end
+
+ def builtin=(bool)
+ @ref.builtin = bool
+ end
+
+ # Retrieve the associated definition and evaluate it.
+ def evaluate
+ if builtin?
+ devfail "Cannot evaluate a builtin type"
+ end
+
+ unless klass = scope.finddefine(self.type)
+ self.fail "Cannot find definition %s" % self.type
+ end
+
+ finish()
+
+ scope.deleteresource(self)
+
+ return klass.evaluate(:scope => scope,
+ :type => self.type,
+ :name => self.title,
+ :arguments => self.to_hash,
+ :scope => self.scope,
+ :exported => self.exported
+ )
+ ensure
+ @evaluated = true
+ end
+
+ def exported=(value)
+ if value
+ @virtual = true
+ @exported = value
+ else
+ @exported = value
+ end
+ end
+
+ def evaluated?
+ if defined? @evaluated and @evaluated
+ true
+ else
+ false
+ end
+ end
+
+ # Do any finishing work on this object, called before evaluation or
+ # before storage/translation.
+ def finish
+ addoverrides()
+ adddefaults()
+ addmetaparams()
+ end
+
+ def initialize(options)
+ options = symbolize_options(options)
+
+ # Collect the options necessary to make the reference.
+ refopts = [:type, :title].inject({}) do |hash, param|
+ hash[param] = options[param] ||
+ devfail("%s must be passed to Resources" % param)
+ options.delete(param)
+ hash
+ end
+
+ @params = {}
+ tmpparams = nil
+ if tmpparams = options[:params]
+ options.delete(:params)
+ end
+
+ # Now set the rest of the options.
+ set_options(options)
+
+ @ref = Reference.new(refopts)
+
+ requiredopts(:scope, :source)
+
+ @ref.scope = self.scope
+
+ if tmpparams
+ tmpparams.each do |param|
+ # We use the method here, because it does type-checking.
+ set(param)
+ end
+ end
+ end
+
+ # Merge an override resource in.
+ def merge(resource)
+ # Some of these might fail, but they'll fail in the way we want.
+ resource.params.each do |name, param|
+ set(param)
+ end
+ end
+
+ # Verify that all passed parameters are valid. This throws an error if there's
+ # a problem, so we don't have to worry about the return value.
+ def paramcheck(param)
+ # This defaults to true
+ unless Puppet[:paramcheck]
+ return
+ end
+
+ return if param == "name" or param == "title" # always allow these
+
+ # FIXME We might need to do more here eventually. Metaparams
+ # behave strangely on containers.
+ return if Puppet::Type.metaparam?(param)
+
+ # Now make sure it's a valid argument to our class.
+ unless @ref.typeclass.validattr?(param)
+ self.fail Puppet::ParseError, "Invalid parameter '%s' for type '%s'" %
+ [param.inspect, @ref.type]
+ end
+ end
+
+ # A temporary occasion, until I get paths in the scopes figured out.
+ def path
+ to_s
+ end
+
+ # Return the short version of our name.
+ def ref
+ @ref.to_s
+ end
+
+ # You have to pass a Resource::Param to this.
+ def set(param)
+ # Because definitions are now parse-time, I can paramcheck immediately.
+ paramcheck(param.name)
+
+ if current = @params[param.name]
+ # XXX Should we ignore any settings that have the same values?
+ if param.source.child_of?(current.source)
+ # Replace it, keeping all of its info.
+ @params[param.name] = param
+ else
+ fail Puppet::ParseError, "Parameter %s is already set on %s by %s" %
+ [param.name, self.to_s, param.source]
+ end
+ else
+ if self.source == param.source or param.source.child_of?(self.source)
+ @params[param.name] = param
+ else
+ fail Puppet::ParseError, "Only subclasses can set parameters"
+ end
+ end
+ end
+
+ # Store our object as a Rails object. We need the host object we're storing it
+ # with.
+ def store(host)
+ args = {}
+ %w{type title tags file line exported}.each do |param|
+ if value = self.send(param)
+ args[param] = value
+ end
+ end
+
+ # 'type' isn't a valid column name, so we have to use something else.
+ args = symbolize_options(args)
+ args[:restype] = args[:type]
+ args.delete(:type)
+
+ # Let's see if the object exists
+ #if obj = host.rails_resources.find_by_type_and_title(self.type, self.title)
+ if obj = host.rails_resources.find_by_restype_and_title(self.type, self.title)
+ # We exist
+ args.each do |param, value|
+ obj[param] = value
+ end
+ else
+ # Else create it anew
+ obj = host.rails_resources.build(args)
+ end
+
+ # Either way, now add our parameters
+ @params.each do |name, param|
+ param.store(obj)
+ end
+
+ return obj
+ end
+
+ def tags
+ unless defined? @tags
+ @tags = scope.tags
+ @tags << self.type
+ end
+ @tags
+ end
+
+ def to_hash
+ @params.inject({}) do |hash, ary|
+ param = ary[1]
+ hash[param.name] = param.value
+ hash
+ end
+ end
+
+ def to_s
+ self.ref
+ end
+
+ # Translate our object to a transportable object.
+ def to_trans
+ unless builtin?
+ devfail "Tried to translate a non-builtin resource"
+ end
+
+ return nil if virtual?
+
+ # Now convert to a transobject
+ obj = Puppet::TransObject.new(@ref.title, @ref.type)
+ to_hash.each do |p, v|
+ if v.is_a?(Reference)
+ v = v.to_ref
+ elsif v.is_a?(Array)
+ v = v.collect { |av|
+ if av.is_a?(Reference)
+ av = av.to_ref
+ end
+ av
+ }
+ end
+ obj[p.to_s] = v
+ end
+
+ obj.file = self.file
+ obj.line = self.line
+
+ obj.tags = self.tags
+
+ return obj
+ end
+
+ def virtual?
+ self.virtual
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/resource/param.rb b/lib/puppet/parser/resource/param.rb
new file mode 100644
index 000000000..259e935d1
--- /dev/null
+++ b/lib/puppet/parser/resource/param.rb
@@ -0,0 +1,44 @@
+# The parameters we stick in Resources.
+class Puppet::Parser::Resource::Param
+ attr_accessor :name, :value, :source, :line, :file
+ include Puppet::Util::Errors
+ include Puppet::Util::MethodHelper
+
+ def initialize(hash)
+ set_options(hash)
+ requiredopts(:name, :value, :source)
+ @name = @name.intern if @name.is_a?(String)
+ end
+
+ def inspect
+ "#<#{self.class} @name => #{self.name}, @value => #{self.value}, @source => #{self.source.type}>"
+ end
+
+ # Store this parameter in a Rails db.
+ def store(resource)
+ args = {}
+ [:name, :value, :line, :file].each do |var|
+ if val = self.send(var)
+ args[var] = val
+ end
+ end
+ args[:name] = args[:name].to_s
+ if obj = resource.rails_parameters.find_by_name(self.name)
+ # We exist
+ args.each do |p, v|
+ obj[p] = v
+ end
+ else
+ # Else create it anew
+ obj = resource.rails_parameters.build(args)
+ end
+
+ return obj
+ end
+
+ def to_s
+ "%s => %s" % [self.name, self.value]
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb
new file mode 100644
index 000000000..2210b71c2
--- /dev/null
+++ b/lib/puppet/parser/resource/reference.rb
@@ -0,0 +1,68 @@
+# A reference to a resource. Mostly just the type and title.
+class Puppet::Parser::Resource::Reference
+ include Puppet::Util::MethodHelper
+ include Puppet::Util::Errors
+
+ attr_accessor :type, :title, :builtin, :file, :line, :scope
+
+ # Are we a builtin type?
+ def builtin?
+ unless defined? @builtin
+ if builtintype()
+ @builtin = true
+ else
+ @builtin = false
+ end
+ end
+
+ self.builtin
+ end
+
+ def builtintype
+ if t = Puppet::Type.type(self.type) and t.name != :component
+ t
+ else
+ nil
+ end
+ end
+
+ # Return the defined type for our obj.
+ def definedtype
+ unless defined? @definedtype
+ if tmp = @scope.finddefine(self.type)
+ @definedtype = tmp
+ else
+ fail Puppet::ParseError, "Could not find definition %s" % self.type
+ end
+ end
+
+ @definedtype
+ end
+
+ def initialize(hash)
+ set_options(hash)
+ requiredopts(:type, :title)
+ end
+
+ def to_ref
+ return [type.to_s,title.to_s]
+ end
+
+ def to_s
+ "%s[%s]" % [type, title]
+ end
+
+ def typeclass
+ unless defined? @typeclass
+ if tmp = builtintype || definedtype
+ @typeclass = tmp
+ else
+ fail Puppet::ParseError, "Could not find type %s" % self.type
+ end
+ end
+
+ @typeclass
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index a26ec938d..9b59ebd64 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -2,1191 +2,645 @@
# such.
require 'puppet/parser/parser'
+require 'puppet/parser/templatewrapper'
require 'puppet/transportable'
-module Puppet::Parser
- class Scope
- class ScopeObj < Hash
- attr_accessor :file, :line, :type, :name
- end
-
- # A simple wrapper for templates, so they don't have full access to
- # the scope objects.
- class TemplateWrapper
- attr_accessor :scope, :file
- include Puppet::Util
- Puppet::Util.logmethods(self)
-
- def initialize(scope, file)
- @scope = scope
- if file =~ /^#{File::SEPARATOR}/
- @file = file
- else
- @file = File.join(Puppet[:templatedir], file)
- end
+class Puppet::Parser::Scope
+ require 'puppet/parser/resource'
- unless FileTest.exists?(@file)
- raise Puppet::ParseError,
- "Could not find template %s" % file
- end
+ AST = Puppet::Parser::AST
- # We'll only ever not have an interpreter in testing, but, eh.
- if @scope.interp
- @scope.interp.newfile(@file)
- end
- end
+ # This doesn't actually work right now.
+ Puppet.config.setdefaults(:puppet,
+ :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."],
+ :templatedir => ["$vardir/templates",
+ "Where Puppet looks for template files."
+ ]
+ )
- # Ruby treats variables like methods, so we can cheat here and
- # trap missing vars like they were missing methods.
- def method_missing(name, *args)
- # We have to tell lookupvar to return :undefined to us when
- # appropriate; otherwise it converts to "".
- value = @scope.lookupvar(name.to_s, false)
- if value != :undefined
- return value
- else
- # Just throw an error immediately, instead of searching for
- # other missingmethod things or whatever.
- raise Puppet::ParseError,
- "Could not find value for '%s'" % name
- end
- end
-
- def result
- result = nil
- benchmark(:debug, "Interpolated template #{@file}") do
- template = ERB.new(File.read(@file), 0, "-")
- result = template.result(binding)
- end
-
- result
- end
-
- def to_s
- "template[%s]" % @file
- end
- end
+ Puppet::Util.logmethods(self)
- # This doesn't actually work right now.
- Puppet.config.setdefaults(:puppet,
- :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."],
- :templatedir => ["$vardir/templates",
- "Where Puppet looks for template files."
- ]
- )
+ include Enumerable
+ include Puppet::Util::Errors
+ attr_accessor :parent, :level, :interp, :source, :host
+ attr_accessor :name, :type, :topscope, :base, :keyword, :namespace
+ attr_accessor :top, :context, :translated, :exported
- Puppet::Util.logmethods(self)
+ # Whether we behave declaratively. Note that it's a class variable,
+ # so all scopes behave the same.
+ @@declarative = true
- include Enumerable
- attr_accessor :parent, :level, :interp
- attr_accessor :name, :type, :topscope, :base, :keyword
+ # Retrieve and set the declarative setting.
+ def self.declarative
+ return @@declarative
+ end
- attr_accessor :top, :context, :translated, :collectable
+ def self.declarative=(val)
+ @@declarative = val
+ end
- # This is probably not all that good of an idea, but...
- # This way a parent can share its tables with all of its children.
- attr_writer :nodetable, :classtable, :definedtable, :exportable
+ # This handles the shared tables that all scopes have. They're effectively
+ # global tables, except that they're only global for a single scope tree,
+ # which is why I can't use class variables for them.
+ def self.sharedtable(*names)
+ attr_accessor(*names)
+ @@sharedtables ||= []
+ @@sharedtables += names
+ end
- # Whether we behave declaratively. Note that it's a class variable,
- # so all scopes behave the same.
- @@declarative = true
+ # This is probably not all that good of an idea, but...
+ # This way a parent can share its tables with all of its children.
+ sharedtable :classtable, :definedtable, :exportable, :overridetable, :collecttable
- # Retrieve and set the declarative setting.
- def self.declarative
- return @@declarative
+ # Is the value true? This allows us to control the definition of truth
+ # in one place.
+ def self.true?(value)
+ if value == false or value == ""
+ return false
+ else
+ return true
end
+ end
- def self.declarative=(val)
- @@declarative = val
+ # Is the type a builtin type?
+ def builtintype?(type)
+ if typeklass = Puppet::Type.type(type)
+ return typeklass
+ else
+ return false
end
+ end
- # Is the value true? This allows us to control the definition of truth
- # in one place.
- def self.true?(value)
- if value == false or value == ""
- return false
- else
- return true
- end
+ # Create a new child scope.
+ def child=(scope)
+ @children.push(scope)
+
+ # Copy all of the shared tables over to the child.
+ @@sharedtables.each do |name|
+ scope.send(name.to_s + "=", self.send(name))
end
+ end
- # Add all of the defaults for a given object to that object.
- def adddefaults(obj)
- defaults = lookupdefaults(obj.type)
+ # Verify that the given object isn't defined elsewhere.
+ def chkobjectclosure(obj)
+ if @definedtable.include?(obj.ref)
+ typeklass = Puppet::Type.type(obj.type)
+ if typeklass and ! typeklass.isomorphic?
+ Puppet.info "Allowing duplicate %s" % type
+ else
+ exobj = @definedtable[obj.ref]
- defaults.each do |var, value|
- unless obj[var]
- self.debug "Adding default %s for %s" %
- [var, obj.type]
+ # Either it's a defined type, which are never
+ # isomorphic, or it's a non-isomorphic type.
+ msg = "Duplicate definition: %s is already defined" % obj.ref
- obj[var] = value
+ if exobj.file and exobj.line
+ msg << " in file %s at line %s" %
+ [exobj.file, exobj.line]
end
- end
- end
- # Add a single object's tags to the global list of tags for
- # that object.
- def addtags(obj)
- unless defined? @tagtable
- raise Puppet::DevError, "Told to add tags, but no tag table"
- end
- list = @tagtable[obj.type][obj.name]
-
- obj.tags.each { |tag|
- unless list.include?(tag)
- if tag.nil? or tag == ""
- Puppet.debug "Got tag %s from %s(%s)" %
- [tag.inspect, obj.type, obj.name]
- else
- list << tag
- end
+ if obj.line or obj.file
+ msg << "; cannot redefine"
end
- }
- end
- # Is the type a builtin type?
- def builtintype?(type)
- if typeklass = Puppet::Type.type(type)
- return typeklass
- else
- return false
+ raise Puppet::ParseError.new(msg)
end
end
- # Verify that the given object isn't defined elsewhere.
- def chkobjectclosure(hash)
- type = hash[:type]
- name = hash[:name]
- unless name
- return true
- end
- if @definedtable[type].include?(name)
- typeklass = Puppet::Type.type(type)
- if typeklass and ! typeklass.isomorphic?
- Puppet.info "Allowing duplicate %s" % type
- else
- exobj = @definedtable[type][name]
-
- # Either it's a defined type, which are never
- # isomorphic, or it's a non-isomorphic type.
- msg = "Duplicate definition: %s[%s] is already defined" %
- [type, name]
-
- if exobj.file and exobj.line
- msg << " in file %s at line %s" %
- [exobj.file, exobj.line]
- end
-
- if hash[:line] or hash[:file]
- msg << "; cannot redefine"
- end
-
- error = Puppet::ParseError.new(msg)
- raise error
- end
- end
-
- return true
- end
-
- def declarative=(val)
- self.class.declarative = val
- end
-
- def declarative
- self.class.declarative
- end
-
- # Log the existing tags. At some point this should be in a better
- # place, but eh.
- def logtags
- @tagtable.sort { |a, b|
- a[0] <=> b[0]
- }.each { |type, names|
- names.sort { |a, b|
- a[0] <=> b[0]
- }.each { |name, tags|
- Puppet.info "%s(%s): '%s'" % [type, name, tags.join("' '")]
- }
- }
- end
+ return true
+ end
- # Create a new child scope.
- def child=(scope)
- @children.push(scope)
+ # Return the list of collections.
+ def collections
+ @collecttable
+ end
- if defined? @nodetable
- scope.nodetable = @nodetable
- else
- raise Puppet::DevError, "No nodetable has been defined"
- end
+ def declarative=(val)
+ self.class.declarative = val
+ end
- if defined? @classtable
- scope.classtable = @classtable
- else
- raise Puppet::DevError, "No classtable has been defined"
- end
+ def declarative
+ self.class.declarative
+ end
- if defined? @exportable
- scope.exportable = @exportable
- else
- raise Puppet::DevError, "No exportable has been defined"
- end
+ # Test whether a given scope is declarative. Even though it's
+ # a global value, the calling objects don't need to know that.
+ def declarative?
+ @@declarative
+ end
- if defined? @definedtable
- scope.definedtable = @definedtable
- else
- raise Puppet::DevError, "No definedtable has been defined"
- end
- end
+ # Remove a specific child.
+ def delete(child)
+ @children.delete(child)
+ end
- # Test whether a given scope is declarative. Even though it's
- # a global value, the calling objects don't need to know that.
- def declarative?
- @@declarative
+ # Remove a resource from the various tables. This is only used when
+ # a resource maps to a definition and gets evaluated.
+ def deleteresource(resource)
+ if @definedtable[resource.ref]
+ @definedtable.delete(resource.ref)
end
- # Remove a specific child.
- def delete(child)
- @children.delete(child)
+ if @children.include?(resource)
+ @children.delete(resource)
end
+ end
- # Are we the top scope?
- def topscope?
- @level == 1
- end
+ # Are we the top scope?
+ def topscope?
+ @level == 1
+ end
- # Return a list of all of the defined classes.
- def classlist
- unless defined? @classtable
- raise Puppet::DevError, "Scope did not receive class table"
- end
- return @classtable.collect { |id, klass|
- # The class table can contain scopes or strings as its values
- # so support them accordingly.
- if klass.is_a? Scope
- klass.type
- else
- klass
- end
- }
+ # Return a list of all of the defined classes.
+ def classlist
+ unless defined? @classtable
+ raise Puppet::DevError, "Scope did not receive class table"
end
+ return @classtable.keys.reject { |k| k == "" }
+ end
- # Yield each child scope in turn
- def each
- @children.each { |child|
- yield child
- }
- end
+ # Yield each child scope in turn
+ def each
+ @children.each { |child|
+ yield child
+ }
+ end
- # Evaluate a list of classes.
- def evalclasses(classes)
- return unless classes
- classes.each do |klass|
- if code = lookuptype(klass)
- # Just reuse the 'include' function, since that's the equivalent
- # of what we're doing here.
- function_include(klass)
- end
+ # Evaluate a list of classes.
+ def evalclasses(*classes)
+ retval = []
+ classes.each do |klass|
+ if obj = findclass(klass)
+ obj.safeevaluate :scope => self
+ retval << klass
end
end
+ end
- # Evaluate a specific node's code. This method will normally be called
- # on the top-level scope, but it actually evaluates the node at the
- # appropriate scope.
- def evalnode(hash)
- objects = hash[:ast]
- names = hash[:names] or
- raise Puppet::DevError, "Node names must be provided to evalnode"
- facts = hash[:facts]
- classes = hash[:classes]
- parent = hash[:parent]
-
- # Always add "default" to our name list, so we're always searching
- # for a default node.
- names << "default"
-
- scope = code = nil
- # Find a node that matches one of our names
- names.each { |node|
- if nodehash = @nodetable[node]
- code = nodehash[:node]
- scope = nodehash[:scope]
-
- if node == "default"
- Puppet.info "Using default node"
- end
- break
- end
- }
-
- # And fail if we don't find one.
- unless scope and code
- raise Puppet::Error, "Could not find configuration for %s" %
- names.join(" or ")
- end
+ def exported?
+ self.exported
+ end
- # We need to do a little skullduggery here. We want a
- # temporary scope, because we don't want this scope to
- # show up permanently in the scope tree -- otherwise we could
- # not evaluate the node multiple times. We could conceivably
- # cache the results, but it's not worth it at this stage.
+ def findclass(name)
+ interp.findclass(namespace, name)
+ end
- # Note that we evaluate the node code with its containing
- # scope, not with the top scope. We also retrieve the created
- # scope so that we can get any classes set within it
- nodescope = code.safeevaluate(:scope => scope, :facts => facts)
+ def finddefine(name)
+ interp.finddefine(namespace, name)
+ end
- scope.evalclasses(classes)
+ def findresource(string, name = nil)
+ if name
+ string = "%s[%s]" % [string, name]
end
- # The top-level evaluate, used to evaluate a whole AST tree. This is
- # a strange method, in that it turns around and calls evaluate() on its
- # :ast argument.
- def evaluate(hash)
- objects = hash[:ast]
- facts = hash[:facts] || {}
-
- @@done = []
-
- unless objects
- raise Puppet::DevError, "Evaluation requires an AST tree"
- end
-
- # Set all of our facts in the top-level scope.
- facts.each { |var, value|
- self.setvar(var, value)
- }
-
- # Evaluate all of our configuration. This does not evaluate any
- # node definitions.
- result = objects.safeevaluate(:scope => self)
-
- # If they've provided a name or a parent, we assume they're looking
- # for nodes.
- if hash[:searched]
- # Specifying a parent node takes precedence, because it is assumed
- # that this node was found in a remote repository like ldap.
- gennode(hash)
- elsif hash.include? :names # else, look for it in the config
- evalnode(hash)
- else
- # Else we're not using nodes at all, so just evaluate any passed-in
- # classes.
- classes = hash[:classes] || []
- evalclasses(classes)
-
- # These classes would be passed in manually, via something like
- # a cfengine module
- end
-
- bucket = self.to_trans
-
- # Add our class list
- unless self.classlist.empty?
- bucket.classes = self.classlist
- end
-
- # Now clean up after ourselves
- [@@done].each do |table|
- table.clear
- end
+ @definedtable[string]
+ end
- return bucket
+ # Recursively complete the whole tree, in preparation for
+ # translation or storage.
+ def finish
+ self.each do |obj|
+ obj.finish
end
+ end
- # Return the hash of objects that we specifically exported. We return
- # a hash to make it easy for the caller to deduplicate based on name.
- def exported(type)
- if @exportable.include?(type)
- return @exportable[type].dup
+ # Initialize our new scope. Defaults to having no parent and to
+ # being declarative.
+ def initialize(hash = {})
+ @parent = nil
+ @type = nil
+ @name = nil
+ @finished = false
+ hash.each { |name, val|
+ method = name.to_s + "="
+ if self.respond_to? method
+ self.send(method, val)
else
- return {}
- end
- end
-
- # Store our object in the central export table.
- def exportobject(obj)
- if @exportable.include?(obj.type) and
- @exportable[obj.type].include?(obj.name)
- raise Puppet::ParseError, "Object %s[%s] is already exported" %
- [obj.type, obj.name]
+ raise Puppet::DevError, "Invalid scope argument %s" % name
end
+ }
- debug "Exporting %s[%s]" % [obj.type, obj.name]
+ @tags = []
- @exportable[obj.type][obj.name] = obj
+ if @parent.nil?
+ unless hash.include?(:declarative)
+ hash[:declarative] = true
+ end
+ self.istop(hash[:declarative])
+ @inside = nil
+ else
+ # This is here, rather than in newchild(), so that all
+ # of the later variable initialization works.
+ @parent.child = self
- return obj
+ @level = @parent.level + 1
+ @interp = @parent.interp
+ @source = hash[:source] || @parent.source
+ @topscope = @parent.topscope
+ @context = @parent.context
+ @inside = @parent.inside
+ @host = @parent.host
+ @type ||= @parent.type
end
- # Pull in all of the appropriate classes and evaluate them. It'd
- # be nice if this didn't know quite so much about how AST::Node
- # operated internally. This is used when a list of classes is passed in,
- # instead of a node definition, such as from the cfengine module.
- def gennode(hash)
- names = hash[:names] or
- raise Puppet::DevError, "Node names must be provided to gennode"
- facts = hash[:facts]
- classes = hash[:classes]
- parent = hash[:parentnode]
- name = names.shift
- arghash = {
- :type => name,
- :code => AST::ASTArray.new(:pin => "[]")
- }
+ # Our child scopes and objects
+ @children = []
- #Puppet.notice "hash is %s" %
- # hash.inspect
- #Puppet.notice "Classes are %s, parent is %s" %
- # [classes.inspect, parent.inspect]
+ # The symbol table for this scope
+ @symtable = Hash.new(nil)
- if parent
- arghash[:parentclass] = parent
- end
+ # All of the defaults set for types. It's a hash of hashes,
+ # with the first key being the type, then the second key being
+ # the parameter.
+ @defaultstable = Hash.new { |dhash,type|
+ dhash[type] = Hash.new(nil)
+ }
- # Create the node
- node = AST::Node.new(arghash)
- node.keyword = "node"
+ # Map the names to the tables.
+ @map = {
+ "variable" => @symtable,
+ "defaults" => @defaultstable
+ }
- # Now evaluate it, which evaluates the parent and nothing else
- # but does return the nodescope.
- scope = node.safeevaluate(:scope => self)
-
- # Finally evaluate our list of classes in this new scope.
- scope.evalclasses(classes)
+ unless @interp
+ raise Puppet::DevError, "Scopes require an interpreter"
end
+ end
- # Initialize our new scope. Defaults to having no parent and to
- # being declarative.
- def initialize(hash = {})
- @parent = nil
- @type = nil
- @name = nil
- @finished = false
- hash.each { |name, val|
- method = name.to_s + "="
- if self.respond_to? method
- self.send(method, val)
- else
- raise Puppet::DevError, "Invalid scope argument %s" % name
- end
- }
-
- @tags = []
-
- if @parent.nil?
- unless hash.include?(:declarative)
- hash[:declarative] = true
- end
- self.istop(hash[:declarative])
- @inside = nil
- else
- # This is here, rather than in newchild(), so that all
- # of the later variable initialization works.
- @parent.child = self
-
- @level = @parent.level + 1
- @interp = @parent.interp
- @topscope = @parent.topscope
- @context = @parent.context
- @inside = @parent.inside
- end
-
- # Our child scopes and objects
- @children = []
-
- # The symbol table for this scope
- @symtable = Hash.new(nil)
+ # Associate the object directly with the scope, so that contained objects
+ # can look up what container they're running within.
+ def inside(arg = nil)
+ return @inside unless arg
+
+ old = @inside
+ @inside = arg
+ yield
+ ensure
+ #Puppet.warning "exiting %s" % @inside.name
+ @inside = old
+ end
- # The type table for this scope
- @typetable = Hash.new(nil)
+ # Mark that we're the top scope, and set some hard-coded info.
+ def istop(declarative = true)
+ # the level is mostly used for debugging
+ @level = 1
- # All of the defaults set for types. It's a hash of hashes,
- # with the first key being the type, then the second key being
- # the parameter.
- @defaultstable = Hash.new { |dhash,type|
- dhash[type] = Hash.new(nil)
- }
+ # The table for storing class singletons. This will only actually
+ # be used by top scopes and node scopes.
+ @classtable = Hash.new(nil)
- # The object table is similar, but it is actually a hash of hashes
- # where the innermost objects are TransObject instances.
- @objectable = Hash.new { |typehash,typekey|
- # See #newobject for how to create the actual objects
- typehash[typekey] = Hash.new(nil)
- }
+ self.class.declarative = declarative
- # This is just for collecting statements locally, so we can
- # verify that there is no overlap within this specific scope
- @localobjectable = Hash.new { |typehash,typekey|
- typehash[typekey] = Hash.new(nil)
- }
+ # The table for all defined objects.
+ @definedtable = {}
- # Map the names to the tables.
- @map = {
- "variable" => @symtable,
- "type" => @typetable,
- "node" => @nodetable,
- "object" => @objectable,
- "defaults" => @defaultstable
- }
- end
+ # The list of objects that will available for export.
+ @exportable = {}
- # Associate the object directly with the scope, so that contained objects
- # can look up what container they're running within.
- def inside(arg = nil)
- return @inside unless arg
-
- old = @inside
- @inside = arg
- yield
- ensure
- #Puppet.warning "exiting %s" % @inside.name
- @inside = old
+ # The list of overrides. This is used to cache overrides on objects
+ # that don't exist yet. We store an array of each override.
+ @overridetable = Hash.new do |overs, ref|
+ overs[ref] = []
end
- # Mark that we're the top scope, and set some hard-coded info.
- def istop(declarative = true)
- # the level is mostly used for debugging
- @level = 1
-
- # The table for storing class singletons. This will only actually
- # be used by top scopes and node scopes.
- @classtable = Hash.new(nil)
+ # Eventually, if we support sites, this will allow definitions
+ # of nodes with the same name in different sites. For now
+ # the top-level scope is always the only site scope.
+ @sitescope = true
- self.class.declarative = declarative
+ @namespace = ""
- # The table for all defined objects.
- @definedtable = Hash.new { |types, type|
- types[type] = {}
- }
-
- # A table for storing nodes.
- @nodetable = Hash.new(nil)
+ # The list of collections that have been created. This is a global list,
+ # but they each refer back to the scope that created them.
+ @collecttable = []
- # The list of objects that will available for export.
- @exportable = Hash.new { |types, type|
- types[type] = {}
- }
+ @context = nil
+ @topscope = self
+ @type = "puppet"
+ @name = "top"
+ end
- # Eventually, if we support sites, this will allow definitions
- # of nodes with the same name in different sites. For now
- # the top-level scope is always the only site scope.
- @sitescope = true
-
- # And create a tag table, so we can collect all of the tags
- # associated with any objects created in this scope tree
- @tagtable = Hash.new { |types, type|
- types[type] = Hash.new { |names, name|
- names[name] = []
- }
+ # Collect all of the defaults set at any higher scopes.
+ # This is a different type of lookup because it's additive --
+ # it collects all of the defaults, with defaults in closer scopes
+ # overriding those in later scopes.
+ def lookupdefaults(type)
+ values = {}
+
+ # first collect the values from the parents
+ unless @parent.nil?
+ @parent.lookupdefaults(type).each { |var,value|
+ values[var] = value
}
-
- @context = nil
- @topscope = self
- @type = "puppet"
- @name = "top"
- end
-
- # Look up a given class. This enables us to make sure classes are
- # singletons
- def lookupclass(klassid)
- unless defined? @classtable
- raise Puppet::DevError, "Scope did not receive class table"
- end
- return @classtable[klassid]
end
- # Collect all of the defaults set at any higher scopes.
- # This is a different type of lookup because it's additive --
- # it collects all of the defaults, with defaults in closer scopes
- # overriding those in later scopes.
- def lookupdefaults(type)
- values = {}
-
- # first collect the values from the parents
- unless @parent.nil?
- @parent.lookupdefaults(type).each { |var,value|
- values[var] = value
- }
- end
-
- # then override them with any current values
- # this should probably be done differently
- if @defaultstable.include?(type)
- @defaultstable[type].each { |var,value|
- values[var] = value
- }
- end
- #Puppet.debug "Got defaults for %s: %s" %
- # [type,values.inspect]
- return values
- end
-
- # Look up all of the exported objects of a given type. Just like
- # lookupobject, this only searches up through parent classes, not
- # the whole scope tree.
- def lookupexported(type)
- found = []
- sub = proc { |table|
- # We always return nil so that it will search all the way
- # up the scope tree.
- if table.has_key?(type)
- table[type].each do |name, obj|
- found << obj
- end
- nil
- else
- info table.keys.inspect
- nil
- end
+ # then override them with any current values
+ # this should probably be done differently
+ if @defaultstable.include?(type)
+ @defaultstable[type].each { |var,value|
+ values[var] = value
}
-
- value = lookup("object",sub, false)
-
- return found
end
- # Look up a node by name
- def lookupnode(name)
- #Puppet.debug "Looking up type %s" % name
- value = lookup("type",name)
- if value == :undefined
- return nil
- else
- #Puppet.debug "Found node %s" % name
- return value
- end
- end
+ #Puppet.debug "Got defaults for %s: %s" %
+ # [type,values.inspect]
+ return values
+ end
- # Look up a defined type.
- def lookuptype(name)
- #Puppet.debug "Looking up type %s" % name
- value = lookup("type",name)
- if value == :undefined
- return nil
- else
- #Puppet.debug "Found type %s" % name
- return value
- end
+ # Look up all of the exported objects of a given type. Just like
+ # lookupobject, this only searches up through parent classes, not
+ # the whole scope tree.
+ def lookupexported(type)
+ @definedtable.find_all do |name, r|
+ r.type == type and r.exported?
end
+ end
- # Look up an object by name and type. This should only look up objects
- # within a class structure, not within the entire scope structure.
- def lookupobject(hash)
- type = hash[:type]
- name = hash[:name]
- #Puppet.debug "Looking up object %s of type %s in level %s" %
- # [name, type, @level]
- sub = proc { |table|
- if table.include?(type)
- if table[type].include?(name)
- table[type][name]
- end
- else
- nil
- end
- }
- value = lookup("object",sub, true)
- if value == :undefined
- return nil
- else
- return value
- end
- end
+ def lookupoverrides(obj)
+ @overridetable[obj.ref]
+ end
- # Look up a variable. The simplest value search we do. Default to returning
- # an empty string for missing values, but support returning a constant.
- def lookupvar(name, usestring = true)
- value = lookup("variable", name)
- if value == :undefined
- if usestring
- return ""
- else
- return :undefined
- end
+ # Look up a defined type.
+ def lookuptype(name)
+ finddefine(name) || findclass(name)
+ end
+
+ # Look up a variable. The simplest value search we do. Default to returning
+ # an empty string for missing values, but support returning a constant.
+ def lookupvar(name, usestring = true)
+ value = lookup("variable", name)
+ if value == :undefined
+ if usestring
+ return ""
else
- return value
+ return :undefined
end
+ else
+ return value
end
+ end
- def newcollection(coll)
- @children << coll
- end
-
- # Add a new object to our object table.
- def newobject(hash)
- if @objectable[hash[:type]].include?(hash[:name])
- raise Puppet::DevError, "Object %s[%s] is already defined" %
- [hash[:type], hash[:name]]
- end
-
- self.chkobjectclosure(hash)
-
- obj = nil
-
- obj = Puppet::TransObject.new(hash[:name], hash[:type])
-
- @children << obj
+ # Add a collection to the global list.
+ def newcollection(coll)
+ @collecttable << coll
+ end
- @objectable[hash[:type]][hash[:name]] = obj
+ # Create a new scope.
+ def newscope(hash = {})
+ hash[:parent] = self
+ #debug "Creating new scope, level %s" % [self.level + 1]
+ return Puppet::Parser::Scope.new(hash)
+ end
- @definedtable[hash[:type]][hash[:name]] = obj
+ # Return the list of remaining overrides.
+ def overrides
+ @overridetable.collect { |name, overs| overs }.flatten
+ end
- return obj
- end
+ def resources
+ @definedtable.values
+ end
- # Create a new scope.
- def newscope(hash = {})
- hash[:parent] = self
- #debug "Creating new scope, level %s" % [self.level + 1]
- return Puppet::Parser::Scope.new(hash)
+ def setclass?(obj)
+ if obj.respond_to?(:fqname)
+ @classtable.has_key?(obj.fqname)
+ else
+ @classtable[obj]
end
+ end
- # Retrieve a specific node. This is used in ast.rb to find a
- # parent node and in findnode to retrieve and evaluate a node.
- def node(name)
- @nodetable[name]
+ # Store the fact that we've evaluated a given class. We use a hash
+ # that gets inherited from the top scope down, rather than a global
+ # hash. We store the object ID, not class name, so that we
+ # can support multiple unrelated classes with the same name.
+ def setclass(obj)
+ if obj.is_a?(AST::HostClass)
+ unless obj.fqname
+ raise Puppet::DevError, "Got a %s with no fully qualified name" %
+ obj.class
+ end
+ @classtable[obj.fqname] = obj
+ else
+ raise Puppet::DevError, "Invalid class %s" % obj.inspect
end
+ end
- # Store the fact that we've evaluated a given class. We use a hash
- # that gets inherited from the top scope down, rather than a global
- # hash. We store the object ID, not class name, so that we
- # can support multiple unrelated classes with the same name.
- def setclass(id, name)
- unless name =~ /^[a-z][\w-]*$/
- raise Puppet::ParseError, "Invalid class name '%s'" % name
- end
+ # Set all of our facts in the top-level scope.
+ def setfacts(facts)
+ facts.each { |var, value|
+ self.setvar(var, value)
+ }
+ end
- @classtable[id] = name
- end
+ # Add a new object to our object table and the global list, and do any necessary
+ # checks.
+ def setresource(obj)
+ self.chkobjectclosure(obj)
- # Store the scope for each class, so that other subclasses can look
- # them up.
- def setscope(id, scope)
- @classtable[id] = scope
- end
+ @children << obj
- # Set defaults for a type. The typename should already be downcased,
- # so that the syntax is isolated.
- def setdefaults(type,params)
- table = @defaultstable[type]
+ # The global table
+ @definedtable[obj.ref] = obj
- # if we got a single param, it'll be in its own array
- unless params[0].is_a?(Array)
- params = [params]
- end
+ return obj
+ end
- params.each { |ary|
- #Puppet.debug "Default for %s is %s => %s" %
- # [type,ary[0].inspect,ary[1].inspect]
- if @@declarative
- if table.include?(ary[0])
- error = Puppet::ParseError.new(
- "Default already defined for %s { %s }" %
- [type,ary[0]]
- )
- raise error
- end
- else
- if table.include?(ary[0])
- # we should maybe allow this warning to be turned off...
- Puppet.warning "Replacing default for %s { %s }" %
- [type,ary[0]]
- end
- end
- table[ary[0]] = ary[1]
- }
+ # Override a parameter in an existing object. If the object does not yet
+ # exist, then cache the override in a global table, so it can be flushed
+ # at the end.
+ def setoverride(resource)
+ resource.override = true
+ if obj = @definedtable[resource.ref]
+ obj.merge(resource)
+ else
+ @overridetable[resource.ref] << resource
end
+ end
- # Store a host in the site node table.
- def setnode(name,code)
- unless defined? @nodetable
- raise Puppet::DevError, "No node table defined"
- end
- if @nodetable.include?(name)
- raise Puppet::ParseError, "Host %s is already defined" % name
+ # Set defaults for a type. The typename should already be downcased,
+ # so that the syntax is isolated. We don't do any kind of type-checking
+ # here; instead we let the resource do it when the defaults are used.
+ def setdefaults(type, params)
+ table = @defaultstable[type]
+
+ # if we got a single param, it'll be in its own array
+ params = [params] unless params.is_a?(Array)
+
+ params.each { |param|
+ #Puppet.debug "Default for %s is %s => %s" %
+ # [type,ary[0].inspect,ary[1].inspect]
+ if @@declarative
+ if table.include?(param.name)
+ self.fail "Default already defined for %s { %s }" %
+ [type,param.name]
+ end
else
- #Puppet.warning "Setting node %s at level %s" % [name, @level]
-
- # We have to store both the scope that's setting the node and
- # the node itself, so that the node gets evaluated in the correct
- # scope.
- code.scope = self
- @nodetable[name] = {
- :scope => self,
- :node => code
- }
+ if table.include?(param.name)
+ # we should maybe allow this warning to be turned off...
+ Puppet.warning "Replacing default for %s { %s }" %
+ [type,param.name]
+ end
end
- end
+ table[param.name] = param
+ }
+ end
- # Define our type.
- def settype(name,ltype)
- unless name
- raise Puppet::DevError, "Got told to set type with a nil type"
- end
- unless ltype
- raise Puppet::DevError, "Got told to set type with a nil object"
- end
- # Don't let them redefine the class in this scope.
- if @typetable.include?(name)
- raise Puppet::ParseError,
- "%s is already defined" % name
- else
- ltype.scope = self
- @typetable[name] = ltype
+ # Set a variable in the current scope. This will override settings
+ # in scopes above, but will not allow variables in the current scope
+ # to be reassigned if we're declarative (which is the default).
+ def setvar(name,value)
+ #Puppet.debug "Setting %s to '%s' at level %s" %
+ # [name.inspect,value,self.level]
+ if @@declarative and @symtable.include?(name)
+ raise Puppet::ParseError, "Cannot reassign variable %s" % name
+ else
+ if @symtable.include?(name)
+ Puppet.warning "Reassigning %s to %s" % [name,value]
end
+ @symtable[name] = value
end
+ end
- # This method will fail if the named object is already defined anywhere
- # in the scope tree, which is what provides some minimal closure-like
- # behaviour.
- def setobject(hash)
- # FIXME This objectlookup stuff should be looking up using both
- # the name and the namevar.
-
- # First see if we can look the object up using normal scope
- # rules, i.e., one of our parent classes has defined the
- # object or something
-
- name = hash[:name]
- type = hash[:type]
- params = hash[:arguments]
- file = hash[:file]
- line = hash[:line]
-
- collectable = hash[:collectable] || self.collectable
-
- # Verify that we're not overriding any already-set parameters.
- if localobj = @localobjectable[type][name]
- params.each { |var, value|
- if localobj.include?(var)
- msg = "Cannot reassign attribute %s on %s[%s]" %
- [var, type, name]
-
- error = Puppet::ParseError.new(msg)
- error.line = line
- error.file = file
- raise error
- end
- }
+ # Return an interpolated string.
+ def strinterp(string)
+ newstring = string.gsub(/\\\$|\$\{(\w+)\}|\$(\w+)/) do |value|
+ # If it matches the backslash, then just retun the dollar sign.
+ if value == '\\$'
+ '$'
+ else # look the variable up
+ var = $1 || $2
+ lookupvar($1 || $2)
end
+ end
- # First look for it in a parent scope
- obj = lookupobject(:name => name, :type => type)
-
- if obj
- unless collectable == obj.collectable
- msg = nil
- if collectable
- msg = "Exported %s[%s] cannot override local objects"
- [type, name]
- else
- msg = "Local %s[%s] cannot override exported objects"
- [type, name]
- end
-
- error = Puppet::ParseError.new(msg)
- error.line = line
- error.file = file
- raise error
- end
- end
+ return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
+ end
- unless obj and obj != :undefined
- unless obj = @objectable[type][name]
- obj = self.newobject(
- :type => type,
- :name => name,
- :line => line,
- :file => file
- )
-
- obj.collectable = collectable
-
- # only set these if we've created the object,
- # which is the most common case
- # FIXME we eventually need to store the file
- # and line with each param, not the object
- # itself.
- obj.file = file
- obj.line = line
- end
+ # Add a tag to our current list. These tags will be added to all
+ # of the objects contained in this scope.
+ def tag(*ary)
+ ary.each { |tag|
+ unless tag =~ /^\w[-\w]+$/
+ fail Puppet::ParseError, "Invalid tag %s" % tag
end
-
- # Now add our parameters. This has the function of overriding
- # existing values, which might have been defined in a higher
- # scope.
- params.each { |var,value|
- # Add it to our found object
- obj[var] = value
- }
-
- # This is only used for override verification -- the local object
- # table does not have transobjects or whatever in it, it just has
- # simple hashes. This is necessary because setobject can modify
- # our object table or a parent class's object table, and we
- # still need to make sure param settings cannot be duplicated
- # within our scope.
- @localobjectable[type][name] ||= {}
- params.each { |var,value|
- # And add it to the local table; mmm, hack
- @localobjectable[type][name][var] = value
- }
-
- return obj
- end
-
- # Set a variable in the current scope. This will override settings
- # in scopes above, but will not allow variables in the current scope
- # to be reassigned if we're declarative (which is the default).
- def setvar(name,value)
- #Puppet.debug "Setting %s to '%s' at level %s" %
- # [name.inspect,value,self.level]
- if @@declarative and @symtable.include?(name)
- raise Puppet::ParseError, "Cannot reassign variable %s" % name
- else
- if @symtable.include?(name)
- Puppet.warning "Reassigning %s to %s" % [name,value]
- end
- @symtable[name] = value
+ if tag.nil? or tag == ""
+ puts caller
+ Puppet.debug "got told to tag with %s" % tag.inspect
+ next
end
- end
-
- # Return an interpolated string.
- def strinterp(string)
- newstring = string.gsub(/\\\$|\$\{(\w+)\}|\$(\w+)/) do |value|
- # If it matches the backslash, then just retun the dollar sign.
- if value == '\\$'
- '$'
- else # look the variable up
- var = $1 || $2
- lookupvar($1 || $2)
- end
+ tag = tag.to_s
+ unless @tags.include?(tag)
+ #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
+ @tags << tag
end
+ }
+ end
- return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
- end
-
- # Add a tag to our current list. These tags will be added to all
- # of the objects contained in this scope.
- def tag(*ary)
- ary.each { |tag|
+ # Return the tags associated with this scope. It's basically
+ # just our parents' tags, plus our type. We don't cache this value
+ # because our parent tags might change between calls.
+ def tags
+ tmp = [] + @tags
+ unless ! defined? @type or @type.nil? or @type == ""
+ tmp << @type.to_s
+ end
+ if @parent
+ #info "Looking for tags in %s" % @parent.type
+ @parent.tags.each { |tag|
if tag.nil? or tag == ""
- Puppet.debug "got told to tag with %s" % tag.inspect
+ Puppet.debug "parent returned tag %s" % tag.inspect
next
end
- unless @tags.include?(tag)
- #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
- @tags << tag.to_s
+ unless tmp.include?(tag)
+ tmp << tag
end
}
end
+ return tmp.sort.uniq
+ end
- # Return the tags associated with this scope. It's basically
- # just our parents' tags, plus our type.
- def tags
- tmp = [] + @tags
- unless ! defined? @type or @type.nil? or @type == ""
- tmp << @type.to_s
- end
- if @parent
- #info "Looking for tags in %s" % @parent.type
- @parent.tags.each { |tag|
- if tag.nil? or tag == ""
- Puppet.debug "parent returned tag %s" % tag.inspect
- next
- end
- unless tmp.include?(tag)
- tmp << tag
- end
- }
- end
- return tmp
+ # Used mainly for logging
+ def to_s
+ if self.name
+ return "%s[%s]" % [@type, @name]
+ else
+ return self.type.to_s
end
+ end
- # Used mainly for logging
- def to_s
- if @name
- return "%s[%s]" % [@type, @name]
+ # Convert all of our objects as necessary.
+ def translate
+ ret = @children.collect do |child|
+ case child
+ when self.class
+ child.translate
+ when Puppet::Parser::Resource
+ child.to_trans
else
- return @type.to_s
+ devfail "Got %s for translation" % child.class
end
+ end.reject { |o| o.nil? }
+ bucket = Puppet::TransBucket.new ret
+ unless self.type
+ devfail "A Scope with no type"
end
+ if @type == ""
+ bucket.type = "main"
+ else
+ bucket.type = @type
+ end
+ if self.name
+ bucket.name = self.name
+ end
+ return bucket
+ end
- # Convert our scope to a TransBucket. Everything in our @localobjecttable
- # gets converted to either an evaluated definition, or a TransObject
- def to_trans
- results = []
-
- # Set this on entry, just in case someone tries to get all weird
- @translated = true
-
- @children.dup.each do |child|
- if @@done.include?(child)
- raise Puppet::DevError, "Already translated %s" %
- child.object_id
- else
- @@done << child
- end
- #warning "Working on %s of type %s with id %s" %
- # [child.type, child.class, child.object_id]
-
- # If it's a scope, then it can only be a subclass's scope, so
- # convert it to a transbucket and store it in our results list
- result = nil
- case child
- when Scope
- result = child.to_trans
- when Puppet::TransObject
- # These objects can map to defined types or builtin types.
- # Builtin types should be passed out as they are, but defined
- # types need to be evaluated. We have to wait until this
- # point so that subclass overrides can happen.
-
- # Wait until the last minute to set tags, although this
- # probably should not matter
- child.tags = self.tags
-
- # Add any defaults.
- self.adddefaults(child)
-
- # Then make sure this child's tags are stored in the
- # central table. This should maybe be in the evaluate
- # methods, but, eh.
- @topscope.addtags(child)
-
- # Now that all that is done, check to see what kind of object
- # it is.
- if objecttype = lookuptype(child.type)
- # It's a defined type, so evaluate it. Retain whether
- # the object is collectable. If the object is collectable,
- # then it will store all of its contents into the
- # @exportable table, rather than returning them.
- result = objecttype.safeevaluate(
- :name => child.name,
- :type => child.type,
- :arguments => child.to_hash,
- :scope => self,
- :collectable => child.collectable
- )
- else
- # If it's collectable, then store it. It will be
- # stripped out in the interpreter using the collectstrip
- # method. If we don't do this, then these objects
- # don't get stored in the DB.
- if child.collectable
- exportobject(child)
- end
- result = child
- end
- # This is pretty hackish, but the collection has to actually
- # be performed after all of the classes and definitions are
- # evaluated, otherwise we won't catch objects that are exported
- # in them. I think this will still be pretty limited in some
- # cases, especially those where you are both exporting and
- # collecting, but it's the best I can do for now.
- when Puppet::Parser::AST::Collection
- child.perform(self).each do |obj|
- results << obj
- end
- else
- raise Puppet::DevError,
- "Puppet::Parse::Scope cannot handle objects of type %s" %
- child.class
- end
-
- # Skip nil objects or empty transbuckets
- if result
- unless result.is_a? Puppet::TransBucket and result.empty?
- results << result
- end
- end
- end
-
- # Get rid of any nil objects.
- results.reject! { |child|
- child.nil?
- }
+ # Undefine a variable; only used for testing.
+ def unsetvar(var)
+ if @symtable.include?(var)
+ @symtable.delete(var)
+ end
+ end
- # If we have a name and type, then make a TransBucket, which
- # becomes a component.
- # Else, just stack all of the objects into the current bucket.
- if @type
- bucket = Puppet::TransBucket.new
+ # Return an array of all of the unevaluated objects
+ def unevaluated
+ ary = @definedtable.find_all do |name, object|
+ ! object.builtin? and ! object.evaluated?
+ end.collect { |name, object| object }
- if defined? @name and @name
- bucket.name = @name
- end
- # it'd be nice not to have to do this...
- results.each { |result|
- #Puppet.warning "Result type is %s" % result.class
- bucket.push(result)
- }
- bucket.type = @type
-
- if defined? @keyword
- bucket.keyword = @keyword
- end
- #Puppet.debug(
- # "TransBucket with name %s and type %s in scope %s" %
- # [@name,@type,self.object_id]
- #)
-
- # now find metaparams
- @symtable.each { |var,value|
- if Puppet::Type.metaparam?(var.intern)
- #Puppet.debug("Adding metaparam %s" % var)
- bucket.param(var,value)
- else
- #Puppet.debug("%s is not a metaparam" % var)
- end
- }
- #Puppet.debug "Returning bucket %s from scope %s" %
- # [bucket.name,self.object_id]
- return bucket
- else
- Puppet.debug "typeless scope; just returning a list"
- return results
- end
+ if ary.empty?
+ return nil
+ else
+ return ary
end
+ end
- # Undefine a variable; only used for testing.
- def unsetvar(var)
- if @symtable.include?(var)
- @symtable.delete(var)
- end
- end
+ protected
- protected
-
- # This method abstracts recursive searching. It accepts the type
- # of search being done and then either a literal key to search for or
- # a Proc instance to do the searching.
- def lookup(type,sub, usecontext = false)
- table = @map[type]
- if table.nil?
- error = Puppet::ParseError.new(
- "Could not retrieve %s table at level %s" %
- [type,self.level]
- )
- raise error
- end
+ # This method abstracts recursive searching. It accepts the type
+ # of search being done and then either a literal key to search for or
+ # a Proc instance to do the searching.
+ def lookup(type,sub, usecontext = false)
+ table = @map[type]
+ if table.nil?
+ self.fail "Could not retrieve %s table at level %s" %
+ [type,self.level]
+ end
- if sub.is_a?(Proc) and obj = sub.call(table)
- return obj
- elsif table.include?(sub)
- return table[sub]
- elsif ! @parent.nil?
- # Context is used for retricting overrides.
- if usecontext and self.context != @parent.context
- return :undefined
- else
- return @parent.lookup(type,sub, usecontext)
- end
- else
+ if sub.is_a?(Proc) and obj = sub.call(table)
+ return obj
+ elsif table.include?(sub)
+ return table[sub]
+ elsif ! @parent.nil?
+ # Context is used for retricting overrides.
+ if usecontext and self.context != @parent.context
return :undefined
+ else
+ return @parent.lookup(type,sub, usecontext)
end
+ else
+ return :undefined
end
end
end
diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb
new file mode 100644
index 000000000..62b45852b
--- /dev/null
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -0,0 +1,58 @@
+# A simple wrapper for templates, so they don't have full access to
+# the scope objects.
+class Puppet::Parser::TemplateWrapper
+ attr_accessor :scope, :file
+ include Puppet::Util
+ Puppet::Util.logmethods(self)
+
+ def initialize(scope, file)
+ @scope = scope
+ if file =~ /^#{File::SEPARATOR}/
+ @file = file
+ else
+ @file = File.join(Puppet[:templatedir], file)
+ end
+
+ unless FileTest.exists?(@file)
+ raise Puppet::ParseError,
+ "Could not find template %s" % file
+ end
+
+ # We'll only ever not have an interpreter in testing, but, eh.
+ if @scope.interp
+ @scope.interp.newfile(@file)
+ end
+ end
+
+ # Ruby treats variables like methods, so we can cheat here and
+ # trap missing vars like they were missing methods.
+ def method_missing(name, *args)
+ # We have to tell lookupvar to return :undefined to us when
+ # appropriate; otherwise it converts to "".
+ value = @scope.lookupvar(name.to_s, false)
+ if value != :undefined
+ return value
+ else
+ # Just throw an error immediately, instead of searching for
+ # other missingmethod things or whatever.
+ raise Puppet::ParseError,
+ "Could not find value for '%s'" % name
+ end
+ end
+
+ def result
+ result = nil
+ benchmark(:debug, "Interpolated template #{@file}") do
+ template = ERB.new(File.read(@file), 0, "-")
+ result = template.result(binding)
+ end
+
+ result
+ end
+
+ def to_s
+ "template[%s]" % @file
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/rails/database.rb b/lib/puppet/rails/database.rb
index caf87cbb8..8be05bd88 100644
--- a/lib/puppet/rails/database.rb
+++ b/lib/puppet/rails/database.rb
@@ -6,20 +6,23 @@ class Puppet::Rails::Database < ActiveRecord::Migration
ActiveRecord::Migration.verbose = false
end
- create_table :rails_objects do |table|
- table.column :name, :string, :null => false
- table.column :ptype, :string, :null => false
+ # 'type' cannot be a column name, apparently
+ create_table :rails_resources do |table|
+ table.column :title, :string, :null => false
+ table.column :restype, :string, :null => false
table.column :tags, :string
table.column :file, :string
table.column :line, :integer
table.column :host_id, :integer
- table.column :collectable, :boolean
+ table.column :exported, :boolean
end
create_table :rails_parameters do |table|
table.column :name, :string, :null => false
table.column :value, :string, :null => false
- table.column :rails_object_id, :integer
+ table.column :file, :string
+ table.column :line, :integer
+ table.column :rails_resource_id, :integer
end
create_table :hosts do |table|
@@ -33,8 +36,10 @@ class Puppet::Rails::Database < ActiveRecord::Migration
end
def self.down
- drop_table :rails_objects
+ drop_table :rails_resources
drop_table :rails_parameters
drop_table :hosts
end
end
+
+# $Id$
diff --git a/lib/puppet/rails/host.rb b/lib/puppet/rails/host.rb
index 77123c871..ccda1af64 100644
--- a/lib/puppet/rails/host.rb
+++ b/lib/puppet/rails/host.rb
@@ -1,11 +1,12 @@
-require 'puppet/rails/rails_object'
+require 'puppet/rails/rails_resource'
#RailsObject = Puppet::Rails::RailsObject
class Puppet::Rails::Host < ActiveRecord::Base
serialize :facts, Hash
serialize :classes, Array
- has_many :rails_objects, :dependent => :delete_all
+ has_many :rails_resources, :dependent => :delete_all,
+ :include => :rails_parameters
# If the host already exists, get rid of its objects
def self.clean(host)
@@ -19,40 +20,43 @@ class Puppet::Rails::Host < ActiveRecord::Base
# Store our host in the database.
def self.store(hash)
- name = hash[:host] || "localhost"
- ip = hash[:ip] || "127.0.0.1"
- facts = hash[:facts] || {}
- objects = hash[:objects]
+ unless hash[:name]
+ raise ArgumentError, "You must specify the hostname for storage"
+ end
- unless objects
- raise ArgumentError, "You must pass objects"
+ args = {}
+ [:name, :facts, :classes].each do |param|
+ if hash[param]
+ args[param] = hash[param]
+ end
end
- hostargs = {
- :name => name,
- :ip => ip,
- :facts => facts,
- :classes => objects.classes
- }
+ if hash[:facts].include?("ipaddress")
+ args[:ip] = hash[:facts]["ipaddress"]
+ end
- objects = objects.flatten
+ unless hash[:resources]
+ raise ArgumentError, "You must pass resources"
+ end
- host = nil
- if host = clean(name)
- [:name, :facts, :classes].each do |param|
- unless host[param] == hostargs[param]
- host[param] = hostargs[param]
+ if host = self.find_by_name(hash[:name])
+ args.each do |param, value|
+ unless host[param] == args[param]
+ host[param] = args[param]
end
end
- host.addobjects(objects)
else
- host = self.new(hostargs) do |hostobj|
- hostobj.addobjects(objects)
- end
+ # Create it anew
+ host = self.new(args)
end
+ hash[:resources].each do |res|
+ res.store(host)
+ end
- host.save
+ Puppet::Util.benchmark(:info, "Saved host to database") do
+ host.save
+ end
return host
end
diff --git a/lib/puppet/rails/rails_object.rb b/lib/puppet/rails/rails_object.rb
deleted file mode 100644
index d1e58e453..000000000
--- a/lib/puppet/rails/rails_object.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'puppet'
-require 'puppet/rails/rails_parameter'
-
-#RailsParameter = Puppet::Rails::RailsParameter
-class Puppet::Rails::RailsObject < ActiveRecord::Base
- has_many :rails_parameters, :dependent => :delete_all
- serialize :tags, Array
-
- belongs_to :host
-
- # Add a set of parameters.
- def addparams(params)
- params.each do |pname, pvalue|
- rails_parameters.build(
- :name => pname,
- :value => pvalue
- )
-
- #self.rails_parameters << pobj
- end
- end
-
- # Convert our object to a trans_object. Do not retain whether the object
- # is collectable, though, since that would cause it to get stripped
- # from the configuration.
- def to_trans
- obj = Puppet::TransObject.new(name(), ptype())
-
- [:file, :line, :tags].each do |method|
- if val = send(method)
- obj.send(method.to_s + "=", val)
- end
- end
- rails_parameters.each do |param|
- obj[param.name] = param.value
- end
-
- return obj
- end
-end
-
-# $Id$
diff --git a/lib/puppet/rails/rails_parameter.rb b/lib/puppet/rails/rails_parameter.rb
index a1966e3dc..295662146 100644
--- a/lib/puppet/rails/rails_parameter.rb
+++ b/lib/puppet/rails/rails_parameter.rb
@@ -1,5 +1,13 @@
class Puppet::Rails::RailsParameter < ActiveRecord::Base
- belongs_to :rails_objects
+ belongs_to :rails_resources
+
+ def to_resourceparam(source)
+ hash = self.attributes
+ hash[:source] = source
+ hash.delete("rails_resource_id")
+ hash.delete("id")
+ Puppet::Parser::Resource::Param.new hash
+ end
end
# $Id$
diff --git a/lib/puppet/rails/rails_resource.rb b/lib/puppet/rails/rails_resource.rb
new file mode 100644
index 000000000..caad1b460
--- /dev/null
+++ b/lib/puppet/rails/rails_resource.rb
@@ -0,0 +1,34 @@
+require 'puppet'
+require 'puppet/rails/rails_parameter'
+
+#RailsParameter = Puppet::Rails::RailsParameter
+class Puppet::Rails::RailsResource < ActiveRecord::Base
+ has_many :rails_parameters, :dependent => :delete_all
+ serialize :tags, Array
+
+ belongs_to :host
+
+ # Convert our object to a resource. Do not retain whether the object
+ # is collectable, though, since that would cause it to get stripped
+ # from the configuration.
+ def to_resource(scope)
+ hash = self.attributes
+ hash["type"] = hash["restype"]
+ hash.delete("restype")
+ hash.delete("host_id")
+ hash.delete("id")
+ hash.each do |p, v|
+ hash.delete(p) if v.nil?
+ end
+ hash[:scope] = scope
+ hash[:source] = scope.source
+ obj = Puppet::Parser::Resource.new(hash)
+ rails_parameters.each do |param|
+ obj.set(param.to_resourceparam(scope.source))
+ end
+
+ return obj
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb
index 90bc70fc2..4b43b9825 100644
--- a/lib/puppet/transportable.rb
+++ b/lib/puppet/transportable.rb
@@ -141,8 +141,8 @@ module Puppet
end.flatten
end
- def initialize
- @children = []
+ def initialize(children = [])
+ @children = children
end
def push(*args)
@@ -219,7 +219,7 @@ module Puppet
else
#Puppet.debug "%s[%s] has no parameters" % [@type, @name]
end
- container = Puppet.type(:component).create(trans)
+ container = Puppet::Type::Component.create(trans)
else
hash = {
:name => self.name,
@@ -238,7 +238,7 @@ module Puppet
#if parent
# hash[:parent] = parent
#end
- container = Puppet.type(:component).create(hash)
+ container = Puppet::Type::Component.create(hash)
end
#Puppet.info container.inspect
@@ -267,8 +267,10 @@ module Puppet
begin
child.to_type(container)
rescue => detail
- # We don't do anything on failures, since we assume it's
- # been logged elsewhere.
+ if Puppet[:trace] and ! detail.is_a?(Puppet::Error)
+ puts detail.backtrace
+ end
+ Puppet.err detail.to_s
end
}
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 320aa83a0..55421eb4c 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -856,9 +856,7 @@ class Type < Puppet::Element
end
def self.validattr?(name)
- if name.is_a?(String)
- name = name.intern
- end
+ name = symbolize(name)
if self.validstate?(name) or self.validparameter?(name) or self.metaparam?(name)
return true
else
@@ -1017,32 +1015,6 @@ class Type < Puppet::Element
}
end
- def devfail(msg)
- self.fail(Puppet::DevError, msg)
- end
-
- # Throw an error, defaulting to a Puppet::Error
- def fail(*args)
- type = nil
- if args[0].is_a?(Class)
- type = args.shift
- else
- type = Puppet::Error
- end
-
- error = type.new(args.join(" "))
-
- if defined? @line and @line
- error.line = @line
- end
-
- if defined? @file and @file
- error.file = @file
- end
-
- raise error
- end
-
# retrieve the 'is' value for a specified state
def is(state)
if @states.include?(state)
@@ -2141,6 +2113,7 @@ class Type < Puppet::Element
# Is the parameter in question a meta-parameter?
def self.metaparam?(param)
+ param = symbolize(param)
@@metaparamhash.include?(param)
end
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index d8b8c812f..755c254d2 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -456,11 +456,13 @@ module Util
end
end
+require 'puppet/util/errors'
require 'puppet/util/methodhelper'
require 'puppet/util/metaid'
require 'puppet/util/classgen'
require 'puppet/util/docs'
require 'puppet/util/execution'
+require 'puppet/util/logging'
require 'puppet/util/package'
require 'puppet/util/warnings'
diff --git a/lib/puppet/util/errors.rb b/lib/puppet/util/errors.rb
new file mode 100644
index 000000000..ad129ee34
--- /dev/null
+++ b/lib/puppet/util/errors.rb
@@ -0,0 +1,55 @@
+# Some helper methods for throwing errors.
+module Puppet::Util::Errors
+ # Throw a dev error.
+ def devfail(msg)
+ self.fail(Puppet::DevError, msg)
+ end
+
+ # Add line and file info if available and appropriate.
+ def adderrorcontext(error, other = nil)
+ error.line ||= self.line if self.respond_to?(:line) and self.line
+ error.file ||= self.file if self.respond_to?(:file) and self.file
+
+ if other and other.respond_to?(:backtrace)
+ error.set_backtrace other.backtrace
+ end
+
+ return error
+ end
+
+ # Wrap a call in such a way that we always throw the right exception and keep
+ # as much context as possible.
+ def exceptwrap(options = {})
+ options[:type] ||= Puppet::DevError
+ begin
+ retval = yield
+ rescue Puppet::Error => detail
+ raise adderrorcontext(detail)
+ rescue => detail
+ message = options[:message] || "%s failed with error %s: %s" %
+ [self.class, detail.class, detail.to_s]
+
+ error = options[:type].new(message)
+ # We can't use self.fail here because it always expects strings,
+ # not exceptions.
+ raise adderrorcontext(error, detail)
+ end
+
+ return retval
+ end
+
+ # Throw an error, defaulting to a Puppet::Error.
+ def fail(*args)
+ if args[0].is_a?(Class)
+ type = args.shift
+ else
+ type = Puppet::Error
+ end
+
+ error = adderrorcontext(type.new(args.join(" ")))
+
+ raise error
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/util/logging.rb b/lib/puppet/util/logging.rb
new file mode 100644
index 000000000..1245e24de
--- /dev/null
+++ b/lib/puppet/util/logging.rb
@@ -0,0 +1,20 @@
+# A module to make logging a bit easier.
+require 'puppet/log'
+
+module Puppet::Util::Logging
+ # Create a method for each log level.
+ Puppet::Log.eachlevel do |level|
+ define_method(level) do |args|
+ if args.is_a?(Array)
+ args = args.join(" ")
+ end
+ Puppet::Log.create(
+ :level => level,
+ :source => self,
+ :message => args
+ )
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/util/methodhelper.rb b/lib/puppet/util/methodhelper.rb
index 5643ac245..c229e8efb 100644
--- a/lib/puppet/util/methodhelper.rb
+++ b/lib/puppet/util/methodhelper.rb
@@ -1,5 +1,26 @@
# Where we store helper methods related to, um, methods.
module Puppet::Util::MethodHelper
+ def requiredopts(*names)
+ names.each do |name|
+ if self.send(name).nil?
+ devfail("%s is a required option for %s" % [name, self.class])
+ end
+ end
+ end
+
+ # Iterate over a hash, treating each member as an attribute.
+ def set_options(options)
+ options.dup.each do |param,value|
+ method = param.to_s + "="
+ unless self.respond_to?(method)
+ self.fail "Invalid parameter %s to object class %s" %
+ [param,self.class.to_s]
+ end
+
+ self.send(method,value)
+ end
+ end
+
# Take a hash and convert all of the keys to symbols if possible.
def symbolize_options(options)
options.inject({}) do |hash, opts|
diff --git a/examples/code/failers/badclassnoparam b/test/data/failers/badclassnoparam
index a0397aacc..a0397aacc 100644
--- a/examples/code/failers/badclassnoparam
+++ b/test/data/failers/badclassnoparam
diff --git a/examples/code/failers/badclassparam b/test/data/failers/badclassparam
index 4c9ff6199..4c9ff6199 100644
--- a/examples/code/failers/badclassparam
+++ b/test/data/failers/badclassparam
diff --git a/examples/code/failers/badcompnoparam b/test/data/failers/badcompnoparam
index fd25c9445..fd25c9445 100644
--- a/examples/code/failers/badcompnoparam
+++ b/test/data/failers/badcompnoparam
diff --git a/examples/code/failers/badcompparam b/test/data/failers/badcompparam
index 346e64b25..346e64b25 100644
--- a/examples/code/failers/badcompparam
+++ b/test/data/failers/badcompparam
diff --git a/examples/code/failers/badtypeparam b/test/data/failers/badtypeparam
index 4634f2052..4634f2052 100644
--- a/examples/code/failers/badtypeparam
+++ b/test/data/failers/badtypeparam
diff --git a/examples/code/failers/noobjectrvalue b/test/data/failers/noobjectrvalue
index ef6064740..ef6064740 100644
--- a/examples/code/failers/noobjectrvalue
+++ b/test/data/failers/noobjectrvalue
diff --git a/examples/code/snippets/aliastest.pp b/test/data/snippets/aliastest.pp
index 2a8fc9cb9..2a8fc9cb9 100644
--- a/examples/code/snippets/aliastest.pp
+++ b/test/data/snippets/aliastest.pp
diff --git a/examples/code/snippets/argumentdefaults b/test/data/snippets/argumentdefaults
index eac9dd757..eac9dd757 100644
--- a/examples/code/snippets/argumentdefaults
+++ b/test/data/snippets/argumentdefaults
diff --git a/examples/code/snippets/casestatement.pp b/test/data/snippets/casestatement.pp
index 4fcf2e408..4fcf2e408 100644
--- a/examples/code/snippets/casestatement.pp
+++ b/test/data/snippets/casestatement.pp
diff --git a/examples/code/snippets/classheirarchy.pp b/test/data/snippets/classheirarchy.pp
index 36619d8b9..36619d8b9 100644
--- a/examples/code/snippets/classheirarchy.pp
+++ b/test/data/snippets/classheirarchy.pp
diff --git a/examples/code/snippets/classincludes.pp b/test/data/snippets/classincludes.pp
index 927800599..927800599 100644
--- a/examples/code/snippets/classincludes.pp
+++ b/test/data/snippets/classincludes.pp
diff --git a/examples/code/snippets/classpathtest b/test/data/snippets/classpathtest
index 68610958b..68610958b 100644
--- a/examples/code/snippets/classpathtest
+++ b/test/data/snippets/classpathtest
diff --git a/examples/code/snippets/componentmetaparams.pp b/test/data/snippets/componentmetaparams.pp
index 1d2c020c3..7d9f0c2c1 100644
--- a/examples/code/snippets/componentmetaparams.pp
+++ b/test/data/snippets/componentmetaparams.pp
@@ -7,5 +7,5 @@ define thing {
}
thing { "/tmp/component2":
- require => file["/tmp/component1"]
+ require => File["/tmp/component1"]
}
diff --git a/examples/code/snippets/deepclassheirarchy.pp b/test/data/snippets/deepclassheirarchy.pp
index 249e6334d..249e6334d 100644
--- a/examples/code/snippets/deepclassheirarchy.pp
+++ b/test/data/snippets/deepclassheirarchy.pp
diff --git a/examples/code/snippets/defineoverrides.pp b/test/data/snippets/defineoverrides.pp
index 1b6561668..a87573a57 100644
--- a/examples/code/snippets/defineoverrides.pp
+++ b/test/data/snippets/defineoverrides.pp
@@ -11,7 +11,7 @@ class base {
}
class sub inherits base {
- myfile { $file: mode => 755 }
+ Myfile[$file] { mode => 755 }
}
include sub
diff --git a/examples/code/snippets/dirchmod b/test/data/snippets/dirchmod
index dc2cc8949..dc2cc8949 100644
--- a/examples/code/snippets/dirchmod
+++ b/test/data/snippets/dirchmod
diff --git a/examples/code/snippets/emptyclass.pp b/test/data/snippets/emptyclass.pp
index 48047e748..48047e748 100644
--- a/examples/code/snippets/emptyclass.pp
+++ b/test/data/snippets/emptyclass.pp
diff --git a/examples/code/snippets/emptyexec.pp b/test/data/snippets/emptyexec.pp
index 847a30d18..847a30d18 100644
--- a/examples/code/snippets/emptyexec.pp
+++ b/test/data/snippets/emptyexec.pp
diff --git a/examples/code/snippets/failmissingexecpath.pp b/test/data/snippets/failmissingexecpath.pp
index ca5b25f4c..aae1a09fa 100644
--- a/examples/code/snippets/failmissingexecpath.pp
+++ b/test/data/snippets/failmissingexecpath.pp
@@ -3,11 +3,11 @@ define distloc($path) {
ensure => file
}
exec { "touch $path":
- subscribe => file["/tmp/exectesting1"],
+ subscribe => File["/tmp/exectesting1"],
refreshonly => true
}
}
-distloc {
+distloc { yay:
path => "/tmp/execdisttesting",
}
diff --git a/examples/code/snippets/falsevalues.pp b/test/data/snippets/falsevalues.pp
index 2143b79a7..2143b79a7 100644
--- a/examples/code/snippets/falsevalues.pp
+++ b/test/data/snippets/falsevalues.pp
diff --git a/examples/code/snippets/filecreate b/test/data/snippets/filecreate
index d7972c234..d7972c234 100644
--- a/examples/code/snippets/filecreate
+++ b/test/data/snippets/filecreate
diff --git a/examples/code/snippets/implicititeration b/test/data/snippets/implicititeration
index 6f34cb29c..6f34cb29c 100644
--- a/examples/code/snippets/implicititeration
+++ b/test/data/snippets/implicititeration
diff --git a/examples/code/snippets/multipleinstances b/test/data/snippets/multipleinstances
index 2f9b3c2e8..2f9b3c2e8 100644
--- a/examples/code/snippets/multipleinstances
+++ b/test/data/snippets/multipleinstances
diff --git a/test/data/snippets/multisubs.pp b/test/data/snippets/multisubs.pp
new file mode 100644
index 000000000..bcec69e2a
--- /dev/null
+++ b/test/data/snippets/multisubs.pp
@@ -0,0 +1,13 @@
+class base {
+ file { "/tmp/multisubtest": content => "base", mode => 644 }
+}
+
+class sub1 inherits base {
+ File["/tmp/multisubtest"] { mode => 755 }
+}
+
+class sub2 inherits base {
+ File["/tmp/multisubtest"] { content => sub2 }
+}
+
+include sub1, sub2
diff --git a/examples/code/snippets/namevartest b/test/data/snippets/namevartest
index dbee1c356..dbee1c356 100644
--- a/examples/code/snippets/namevartest
+++ b/test/data/snippets/namevartest
diff --git a/examples/code/snippets/scopetest b/test/data/snippets/scopetest
index 3d3b31d8a..331491766 100644
--- a/examples/code/snippets/scopetest
+++ b/test/data/snippets/scopetest
@@ -2,12 +2,12 @@
$mode = 640
define thing {
- file { "/tmp/scopetest": ensure => file, mode => $mode }
+ file { "/tmp/$name": ensure => file, mode => $mode }
}
class testing {
$mode = 755
- thing {}
+ thing {scopetest: }
}
include testing
diff --git a/examples/code/snippets/selectorvalues.pp b/test/data/snippets/selectorvalues.pp
index cd8cf77a7..cd8cf77a7 100644
--- a/examples/code/snippets/selectorvalues.pp
+++ b/test/data/snippets/selectorvalues.pp
diff --git a/examples/code/snippets/simpledefaults b/test/data/snippets/simpledefaults
index 63d199a68..63d199a68 100644
--- a/examples/code/snippets/simpledefaults
+++ b/test/data/snippets/simpledefaults
diff --git a/examples/code/snippets/simpleselector b/test/data/snippets/simpleselector
index 8b9bc7292..8b9bc7292 100644
--- a/examples/code/snippets/simpleselector
+++ b/test/data/snippets/simpleselector
diff --git a/examples/code/snippets/singleary.pp b/test/data/snippets/singleary.pp
index 1a6aebb21..9ce56dd89 100644
--- a/examples/code/snippets/singleary.pp
+++ b/test/data/snippets/singleary.pp
@@ -10,10 +10,10 @@ file { "/tmp/singleary2":
file { "/tmp/singleary3":
ensure => file,
- require => [file["/tmp/singleary1"], file["/tmp/singleary2"]]
+ require => [File["/tmp/singleary1"], File["/tmp/singleary2"]]
}
file { "/tmp/singleary4":
ensure => file,
- require => [file["/tmp/singleary1"]]
+ require => [File["/tmp/singleary1"]]
}
diff --git a/examples/code/snippets/singlequote.pp b/test/data/snippets/singlequote.pp
index dc876a2f8..dc876a2f8 100644
--- a/examples/code/snippets/singlequote.pp
+++ b/test/data/snippets/singlequote.pp
diff --git a/examples/code/snippets/singleselector.pp b/test/data/snippets/singleselector.pp
index 520a14017..520a14017 100644
--- a/examples/code/snippets/singleselector.pp
+++ b/test/data/snippets/singleselector.pp
diff --git a/examples/code/snippets/tag.pp b/test/data/snippets/tag.pp
index e6e770dd9..e6e770dd9 100644
--- a/examples/code/snippets/tag.pp
+++ b/test/data/snippets/tag.pp
diff --git a/examples/code/snippets/tagged.pp b/test/data/snippets/tagged.pp
index e3ca93838..7bf90a645 100644
--- a/examples/code/snippets/tagged.pp
+++ b/test/data/snippets/tagged.pp
@@ -3,7 +3,7 @@
tag testing
tag(funtest)
-define tagdefine {
+class tagdefine {
$path = tagged(tagdefine) ? {
true => "true", false => "false"
}
@@ -11,7 +11,7 @@ define tagdefine {
file { "/tmp/taggeddefine$path": ensure => file }
}
-tagdefine {}
+include tagdefine
$yayness = tagged(yayness) ? {
true => "true", false => "false"
diff --git a/test/executables/puppetmodule.rb b/test/executables/puppetmodule.rb
index 0752fed14..6ffd84be3 100755
--- a/test/executables/puppetmodule.rb
+++ b/test/executables/puppetmodule.rb
@@ -34,16 +34,19 @@ class TestPuppetModule < Test::Unit::TestCase
if Puppet[:debug]
cmd += " --logdest %s" % "console"
cmd += " --debug"
+ cmd += " --trace"
else
cmd += " --logdest %s" % "/dev/null"
end
ENV["CFALLCLASSES"] = "yaytest:all"
+ libsetup
+ out = nil
assert_nothing_raised {
- %x{#{cmd + " " + file} 2>&1}
+ out = %x{#{cmd + " " + file} 2>&1}
}
- assert($? == 0, "Puppet module exited with code %s" % $?.to_i)
+ assert($? == 0, "Puppet module exited with code %s: %s" % [$?.to_i, out])
assert(FileTest.exists?(createdfile), "Failed to create config'ed file")
end
diff --git a/test/language/ast.rb b/test/language/ast.rb
index ab528a49d..731b37cb8 100755
--- a/test/language/ast.rb
+++ b/test/language/ast.rb
@@ -1,13 +1,19 @@
#!/usr/bin/ruby
require 'puppet'
+require 'puppet/rails'
require 'puppet/parser/interpreter'
require 'puppet/parser/parser'
require 'puppet/client'
require 'puppettest'
+require 'puppettest/resourcetesting'
+require 'puppettest/parsertesting'
+require 'puppettest/railstesting'
class TestAST < Test::Unit::TestCase
+ include PuppetTest::RailsTesting
include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
# A fake class that we can use for testing evaluation.
class FakeAST
@@ -28,910 +34,465 @@ class TestAST < Test::Unit::TestCase
end
end
- # Test that classes behave like singletons
- def test_classsingleton
- parent = child1 = child2 = nil
- children = []
-
- # create the parent class
- children << classobj("parent")
-
- # Create child class one
- children << classobj("child1", :parentclass => nameobj("parent"))
+ if defined? ActiveRecord
+ # Verify that our collection stuff works.
+ def test_collection
+ collectable = []
+ non = []
+ # First put some objects into the database.
+ bucket = mk_transtree do |object, depth, width|
+ # and mark some of them collectable
+ if width % 2 == 1
+ object.collectable = true
+ collectable << object
+ else
+ non << object
+ end
+ end
- # Create child class two
- children << classobj("child2", :parentclass => nameobj("parent"))
- classes = %w{parent child1 child2}
+ # Now collect our facts
+ facts = {}
+ Facter.each do |fact, value| facts[fact] = value end
- # Now call the two classes
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("child1"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
- children << AST::ObjectDef.new(
- :type => nameobj("child2"),
- :name => nameobj("booness"),
- :params => astarray()
- )
+ assert_nothing_raised {
+ Puppet::Rails.init
}
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
+ # Now try storing our crap
+ assert_nothing_raised {
+ host = Puppet::Rails::Host.store(
+ :objects => bucket,
+ :facts => facts,
+ :host => facts["hostname"]
)
}
- scope = nil
- objects = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
+ # Now create an ast tree that collects that. They should all be files.
+ coll = nil
+ assert_nothing_raised {
+ coll = AST::Collection.new(
+ :type => nameobj("file")
+ )
}
- assert_instance_of(Puppet::TransBucket, objects)
-
- assert_equal(1, scope.find_all { |child|
- if child.is_a? Puppet::Parser::Scope
- child.lookupobject(:name => "/parent", :type => "file")
- else
- nil
- end
- }.length, "Found incorrect number of '/parent' objects")
-
- assert_equal(classes.sort, scope.classlist.sort)
- end
-
- # Test that 'tagobject' collects all of an object's parameters and stores
- # them in one TransObject, rather than many. This is probably a bad idea.
- def test_tagobject
top = nil
- children = [
- fileobj("/etc", "owner" => "root"),
- fileobj("/etc", "group" => "root")
- ]
assert_nothing_raised("Could not create top object") {
top = AST::ASTArray.new(
- :children => children
+ :children => [coll]
)
}
- scope = Puppet::Parser::Scope.new()
+ objects = nil
assert_nothing_raised("Could not evaluate") {
- top.evaluate(:scope => scope)
- }
-
- obj = nil
- assert_nothing_raised("Could not retrieve file object") {
- obj = scope.lookupobject(:name => "/etc", :type => "file")
- }
-
- assert(obj, "could not retrieve file object")
-
- %w{owner group}.each { |param|
- assert(obj.include?(param), "Object did not include %s" % param)
+ scope = mkscope
+ objects = scope.evaluate(:ast => top).flatten
}
+ assert(objects.length > 0, "Did not receive any collected objects")
+ end
+ else
+ $stderr.puts "No ActiveRecord -- skipping collection tests"
end
- # Verify that objects can only have parents of the same type.
- def test_validparent
- parent = child1 = nil
- children = []
-
- # create the parent class
- children << compobj("parent", :args => AST::ASTArray.new(:children => []))
-
- # Create child class one
- children << classobj("child1", :parentclass => nameobj("parent"))
+ def test_if
+ astif = nil
+ astelse = nil
+ fakeelse = FakeAST.new(:else)
+ faketest = FakeAST.new(true)
+ fakeif = FakeAST.new(:if)
- # Now call the two classes
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("child1"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
+ assert_nothing_raised {
+ astelse = AST::Else.new(:statements => fakeelse)
}
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
+ assert_nothing_raised {
+ astif = AST::IfStatement.new(
+ :test => faketest,
+ :statements => fakeif,
+ :else => astelse
)
}
- scope = nil
- assert_raise(Puppet::ParseError, "Invalid parent type was allowed") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
+ # We initialized it to true, so we should get that first
+ ret = nil
+ assert_nothing_raised {
+ ret = astif.evaluate(:scope => "yay")
}
- end
-
- # Verify that nodes don't evaluate code in other node scopes but that their
- # facts work outside their scopes.
- def test_nodescopes
- parent = child1 = nil
- topchildren = []
-
- # create the parent class
- topchildren << classobj("everyone")
-
- topchildren << classobj("parent")
-
-
- classes = %w{everyone parent}
+ assert_equal(:if, ret)
- # And a variable, so we verify the facts get set at the top
+ # Now set it to false and check that
+ faketest.evaluate = false
assert_nothing_raised {
- children = []
- children << varobj("yaytest", "$hostname")
+ ret = astif.evaluate(:scope => "yay")
}
+ assert_equal(:else, ret)
+ end
- nodes = []
-
- 3.times do |i|
- children = []
-
- # Create a child class
- topchildren << classobj("perchild#{i}", :parentclass => nameobj("parent"))
- classes << "perchild%s"
-
- # Create a child class
- children << classobj("child", :parentclass => nameobj("parent"))
-
- classes << "child"
-
- ["child", "everyone", "perchild#{i}"].each do |name|
- # Now call our child class
- assert_nothing_raised {
- children << AST::ObjectDef.new(
- :type => nameobj(name),
- :params => astarray()
- )
- }
- end
+ # Make sure our override object behaves "correctly"
+ def test_override
+ interp, scope, source = mkclassframing
- # and another variable
- assert_nothing_raised {
- children << varobj("rahtest", "$hostname")
- }
-
- # create the node
- nodename = "node#{i}"
- nodes << nodename
- assert_nothing_raised("Could not create parent object") {
- topchildren << AST::NodeDef.new(
- :names => nameobj(nodename),
- :code => AST::ASTArray.new(
- :children => children
- )
- )
- }
+ ref = nil
+ assert_nothing_raised do
+ ref = resourceoverride("resource", "yaytest", "one" => "yay", "two" => "boo")
end
- # Create the wrapper object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => topchildren
- )
- }
-
- nodes.each_with_index do |node, i|
- # Evaluate the parse tree
- scope = Puppet::Parser::Scope.new()
- args = {:names => [node], :facts => {"hostname" => node}, :ast => top}
+ ret = nil
+ assert_nothing_raised do
+ ret = ref.evaluate :scope => scope
+ end
- # verify that we can evaluate it okay
- trans = nil
- assert_nothing_raised("Could not retrieve node definition") {
- trans = scope.evaluate(args)
- }
+ assert_instance_of(Puppet::Parser::Resource, ret)
- assert_equal(node, scope.lookupvar("hostname"))
+ assert(ret.override?, "Resource was not an override resource")
- assert(trans, "Could not retrieve trans objects")
+ assert(scope.overridetable[ret.ref].include?(ret),
+ "Was not stored in the override table")
+ end
- # and that we can convert them to type objects
- objects = nil
- assert_nothing_raised("Could not retrieve node definition") {
- objects = trans.to_type
- }
+ # make sure our resourcedefaults ast object works correctly.
+ def test_resourcedefaults
+ interp, scope, source = mkclassframing
- assert(objects, "Could not retrieve trans objects")
+ # Now make some defaults for files
+ args = {:source => "/yay/ness", :group => "yayness"}
+ assert_nothing_raised do
+ obj = defaultobj "file", args
+ obj.evaluate :scope => scope
+ end
- count = 0
- # Make sure the node name gets into the path correctly.
- Puppet.type(:file).each { |obj|
- count += 1
- assert(obj.path !~ /#{node}\[#{node}\]/,
- "Node name appears twice")
- }
+ hash = nil
+ assert_nothing_raised do
+ hash = scope.lookupdefaults("file")
+ end
- assert(count > 0, "Did not create any files")
+ hash.each do |name, value|
+ assert_instance_of(Symbol, name) # params always convert
+ assert_instance_of(Puppet::Parser::Resource::Param, value)
+ end
- classes.each do |name|
- if name =~ /%s/
- name = name % i
- end
- assert(Puppet::Type.type(:file)["/#{name}"], "Could not find '#{name}'")
- end
- Puppet::Type.allclear
+ args.each do |name, value|
+ assert(hash[name], "Did not get default %s" % name)
+ assert_equal(value, hash[name].value)
end
end
- # Verify that classes are correctly defined in node scopes.
- def disabled_test_nodeclasslookup
- parent = child1 = nil
- children = []
-
- # create the parent class
- children << classobj("parent")
-
- # Create child class one
- children << classobj("child1", :parentclass => nameobj("parent"))
-
- # Now call the two classes
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("child1"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
- }
-
- # create the node
- nodename = "mynodename"
- node = nil
- assert_nothing_raised("Could not create parent object") {
- node = AST::NodeDef.new(
- :names => nameobj(nodename),
- :code => AST::ASTArray.new(
- :children => children
- )
- )
- }
+ def test_hostclass
+ interp, scope, source = mkclassframing
- # Create the wrapper object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => [node]
+ # Create the class we're testing, first with no parent
+ klass = interp.newclass "first",
+ :code => AST::ASTArray.new(
+ :children => [resourcedef("file", "/tmp",
+ "owner" => "nobody", "mode" => "755")]
)
- }
-
- # Evaluate the parse tree
- scope = nil
- assert_nothing_raised("Could not evaluate node") {
- scope = Puppet::Parser::Scope.new()
- top.evaluate(:scope => scope)
- }
-
- # Verify that, well, nothing really happened, and especially verify
- # that the top scope is not a node scope
- assert(scope.topscope?, "Scope is not top scope")
- assert(! scope.nodescope?, "Scope is mistakenly node scope")
- assert(! scope.lookupclass("parent"), "Found parent class in top scope")
- # verify we can find our node
- assert(scope.node(nodename), "Could not find node")
-
- # And verify that we can evaluate it okay
- objects = nil
- assert_nothing_raised("Could not retrieve node definition") {
- objects = scope.evalnode(:name => [nodename], :facts => {})
- }
-
- assert(objects, "Could not retrieve node definition")
-
- # Because node scopes are temporary (i.e., they get destroyed after the node's
- # config is returned) we should not be able to find the node scope.
- nodescope = nil
- assert_nothing_raised {
- nodescope = scope.find { |child|
- child.nodescope?
- }
- }
-
- assert_nil(nodescope, "Found nodescope")
-
- # And now verify again that the top scope cannot find the node's definition
- # of the parent class
- assert(! scope.lookupclass("parent"), "Found parent class in top scope")
-
- trans = nil
- # Verify that we can evaluate the node twice
- assert_nothing_raised("Could not retrieve node definition") {
- trans = scope.evalnode(:name => [nodename], :facts => {})
- }
-
- objects = nil
- assert_nothing_raised("Could not convert to objects") {
- objects = trans.to_type
- }
-
- Puppet.type(:file).each { |obj|
- assert(obj.path !~ /#{nodename}\[#{nodename}\]/,
- "Node name appears twice")
- }
-
- assert(Puppet::Type.type(:file)["/child1"], "Could not find child")
- assert(Puppet::Type.type(:file)["/parent"], "Could not find parent")
- end
+ assert_nothing_raised do
+ klass.evaluate(:scope => scope)
+ end
- # Test that you can look a host up using multiple names, e.g., an FQDN and
- # a short name
- def test_multiplenodenames
- children = []
+ # Then try it again
+ assert_nothing_raised do
+ klass.evaluate(:scope => scope)
+ end
- # create a short-name node
- shortname = "mynodename"
- children << nodedef(shortname)
+ assert(scope.setclass?(klass), "Class was not considered evaluated")
- # And a long-name node
- longname = "node.domain.com"
- children << nodedef(longname)
+ tmp = scope.findresource("file[/tmp]")
+ assert(tmp, "Could not find file /tmp")
+ assert_equal("nobody", tmp[:owner])
+ assert_equal("755", tmp[:mode])
- # Create the wrapper object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- # Evaluate the parse tree
- scope = Puppet::Parser::Scope.new()
-
- # Verify we can find the node via a search list
- objects = nil
- assert_nothing_raised("Could not retrieve short node definition") {
- objects = scope.evaluate(
- :names => ["%s.domain.com" % shortname, shortname], :facts => {},
- :ast => top
+ # Now create a couple more classes.
+ newbase = interp.newclass "newbase",
+ :code => AST::ASTArray.new(
+ :children => [resourcedef("file", "/tmp/other",
+ "owner" => "nobody", "mode" => "644")]
)
- }
- assert(objects, "Could not retrieve short node definition")
-
- scope = Puppet::Parser::Scope.new()
- # and then look for the long name
- assert_nothing_raised("Could not retrieve long node definition") {
- objects = scope.evaluate(
- :names => [longname.sub(/\..+/, ''), longname], :facts => {},
- :ast => top
+ newsub = interp.newclass "newsub",
+ :parent => "newbase",
+ :code => AST::ASTArray.new(
+ :children => [resourcedef("file", "/tmp/yay",
+ "owner" => "nobody", "mode" => "755"),
+ resourceoverride("file", "/tmp/other",
+ "owner" => "daemon")
+ ]
)
- }
- assert(objects, "Could not retrieve long node definition")
- end
-
- # Test that a node gets the entire configuration except for work meant for
- # another node
- def test_fullconfigwithnodes
- children = []
-
- children << fileobj("/testing")
- # create a short-name node
- name = "mynodename"
- children << nodedef(name)
-
- # Create the wrapper object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
+ # Override a different variable in the top scope.
+ moresub = interp.newclass "moresub",
+ :parent => "newbase",
+ :code => AST::ASTArray.new(
+ :children => [resourceoverride("file", "/tmp/other",
+ "mode" => "755")]
)
- }
- scope = Puppet::Parser::Scope.new()
-
- # Verify we can find the node via a search list
- objects = nil
- assert_nothing_raised("Could not retrieve short node definition") {
- objects = scope.evaluate(:names => [name], :facts => {}, :ast => top)
- }
- assert(objects, "Could not retrieve short node definition")
- assert_instance_of(Puppet::TransBucket, objects)
-
- # And now verify that we got both the top and node objects
- assert_nothing_raised("Could not find top-declared object") {
- assert_equal("/testing", objects[0].name)
- }
-
- assert_nothing_raised("Could not find node-declared object %s" %
- "/%s" % name
- ) {
- assert_equal("/%s" % name, objects[1][0].name)
- }
- end
-
- # Test that we can 'include' variables, not just normal strings.
- def test_includevars
- children = []
- classes = []
-
- # Create our class for testin
- klassname = "include"
- children << classobj(klassname)
- classes << klassname
-
- # Then add our variable assignment
- children << varobj("klassvar", klassname)
-
- # And finally add our calling of the variable
- children << AST::ObjectDef.new(
- :type => AST::Variable.new(:value => "klassvar"),
- :params => astarray
- )
-
- # And then create our top object
- top = AST::ASTArray.new(
- :children => children
- )
+ assert_nothing_raised do
+ newsub.evaluate(:scope => scope)
+ end
- # Evaluate the parse tree
- scope = nil
- objects = nil
- assert_nothing_raised("Could not evaluate node") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
+ assert_nothing_raised do
+ moresub.evaluate(:scope => scope)
+ end
- # Verify we get the right classlist back
- assert_equal(classes.sort, scope.classlist.sort)
+ assert(scope.setclass?(newbase), "Did not eval newbase")
+ assert(scope.setclass?(newsub), "Did not eval newsub")
- # Verify we can find the node via a search list
- #assert_nothing_raised("Could not retrieve objects") {
- # objects = scope.to_trans
- #}
- assert(objects, "Could not retrieve objects")
+ yay = scope.findresource("file[/tmp/yay]")
+ assert(yay, "Did not find file /tmp/yay")
+ assert_equal("nobody", yay[:owner])
+ assert_equal("755", yay[:mode])
- assert_nothing_raised("Could not find top-declared object") {
- assert_equal("/%s" % klassname, objects[0][0].name)
- }
+ other = scope.findresource("file[/tmp/other]")
+ assert(other, "Did not find file /tmp/other")
+ assert_equal("daemon", other[:owner])
+ assert_equal("755", other[:mode])
end
- # Test that node inheritance works correctly
- def test_nodeinheritance
- children = []
+ def test_component
+ interp, scope, source = mkclassframing
- # create the base node
- name = "basenode"
- children << nodedef(name)
-
- # and the sub node
- name = "subnode"
- children << AST::NodeDef.new(
- :names => nameobj(name),
- :parentclass => nameobj("basenode"),
+ # Create a new definition
+ klass = interp.newdefine "yayness",
+ :arguments => [["owner", stringobj("nobody")], %w{mode}],
:code => AST::ASTArray.new(
- :children => [
- varobj("%svar" % name, "%svalue" % name),
- fileobj("/%s" % name)
- ]
+ :children => [resourcedef("file", "/tmp/$name",
+ "owner" => varref("owner"), "mode" => varref("mode"))]
)
- )
- #subnode = nodedef(name)
- #subnode.parentclass = "basenode"
- #children << subnode
+ # Test validattr? a couple different ways
+ [:owner, "owner", :schedule, "schedule"].each do |var|
+ assert(klass.validattr?(var), "%s was not considered valid" % var.inspect)
+ end
- # and the top object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
+ [:random, "random"].each do |var|
+ assert(! klass.validattr?(var), "%s was considered valid" % var.inspect)
+ end
+ # Now call it a couple of times
+ # First try it without a required param
+ assert_raise(Puppet::ParseError) do
+ klass.evaluate(:scope => scope,
+ :name => "bad",
+ :arguments => {"owner" => "nobody"}
)
- }
+ end
- # Evaluate the parse tree
- scope = Puppet::Parser::Scope.new()
+ # And make sure it didn't create the file
+ assert_nil(scope.findresource("file[/tmp/bad]"),
+ "Made file with invalid params")
- # Verify we can find the node via a search list
- objects = nil
- assert_nothing_raised("Could not evaluate node") {
- objects = scope.evaluate(:names => [name], :facts => {}, :ast => top)
- }
- assert(objects, "Could not retrieve node definition")
-
- assert_nothing_raised {
- inner = objects[0]
-
- # And now verify that we got the subnode file
- assert_nothing_raised("Could not find basenode file") {
- base = inner[0]
- assert_equal("/basenode", base.name)
- }
-
- # and the parent node file
- assert_nothing_raised("Could not find subnode file") {
- sub = inner[1]
- assert_equal("/subnode", sub.name)
- }
-
- inner.each { |obj|
- %w{basenode subnode}.each { |tag|
- assert(obj.tags.include?(tag),
- "%s did not include %s tag" % [obj.name, tag]
- )
- }
- }
- }
- end
-
- def test_typechecking
- object = nil
- children = []
- type = "deftype"
- assert_nothing_raised("Could not add AST nodes for calling") {
- object = AST::ObjectDef.new(
- :type => nameobj(type),
- :name => nameobj("yayness"),
- :params => astarray()
+ assert_nothing_raised do
+ klass.evaluate(:scope => scope,
+ :name => "first",
+ :arguments => {"mode" => "755"}
)
- }
-
- assert_nothing_raised("Typecheck failed") {
- object.typecheck(type)
- }
-
- # Add a scope, which makes it think it's evaluating
- assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
- object.scope = scope
- }
+ end
- # Verify an error is thrown when it can't find the type
- assert_raise(Puppet::ParseError) {
- object.typecheck(type)
- }
+ firstobj = scope.findresource("file[/tmp/first]")
+ assert(firstobj, "Did not create /tmp/first obj")
- # Create child class one
- children << classobj(type)
- children << object
+ assert_equal("file", firstobj.type)
+ assert_equal("/tmp/first", firstobj.title)
+ assert_equal("nobody", firstobj[:owner])
+ assert_equal("755", firstobj[:mode])
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
+ # Make sure we can't evaluate it with the same args
+ assert_raise(Puppet::ParseError) do
+ klass.evaluate(:scope => scope,
+ :name => "first",
+ :arguments => {"mode" => "755"}
)
- }
-
- scope = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = top.evaluate(:scope => scope)
- }
- end
+ end
- def disabled_test_paramcheck
- object = nil
- children = []
- type = "deftype"
- params = %w{param1 param2}
-
- comp = compobj(type, {
- :args => astarray(
- argobj("param1", "yay"),
- argobj("param2", "rah")
- ),
- :code => AST::ASTArray.new(
- :children => [
- varobj("%svar" % name, "%svalue" % name),
- fileobj("/%s" % name)
- ]
+ # Now create another with different args
+ assert_nothing_raised do
+ klass.evaluate(:scope => scope,
+ :name => "second",
+ :arguments => {"mode" => "755", "owner" => "daemon"}
)
- })
- assert_nothing_raised("Could not add AST nodes for calling") {
- object = AST::ObjectDef.new(
- :type => nameobj(type),
- :name => nameobj("yayness"),
- :params => astarray(
- astarray(stringobj("param1"), stringobj("value1")),
- astarray(stringobj("param2"), stringobj("value2"))
- )
- )
- }
-
- # Add a scope, which makes it think it's evaluating
- assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
- object.scope = scope
- }
-
- # Verify an error is thrown when it can't find the type
- assert_raise(Puppet::ParseError) {
- object.paramcheck(false, comp)
- }
+ end
- # Create child class one
- children << classobj(type)
- children << object
+ secondobj = scope.findresource("file[/tmp/second]")
+ assert(secondobj, "Did not create /tmp/second obj")
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- scope = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = top.evaluate(:scope => scope)
- }
+ assert_equal("file", secondobj.type)
+ assert_equal("/tmp/second", secondobj.title)
+ assert_equal("daemon", secondobj[:owner])
+ assert_equal("755", secondobj[:mode])
end
- def test_setclass
- type = "yay"
- classes = [type]
- children = []
- # Create child class one
- children << varobj("variable", "aclass")
- children << tagobj(type, varref("variable"))
- children << tagobj(type)
+ def test_node
+ interp = mkinterp
+ scope = mkscope(:interp => interp)
- classes << "aclass"
+ # Define a base node
+ basenode = interp.newnode "basenode", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/base", "owner" => "root")
+ ])
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
+ # Now define a subnode
+ nodes = interp.newnode ["mynode", "othernode"],
+ :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/mynode", "owner" => "root"),
+ resourcedef("file", "/tmp/basenode", "owner" => "daemon")
+ ])
- scope = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = top.evaluate(:scope => scope)
- }
+ assert_instance_of(Array, nodes)
+ # Make sure we can find them all.
+ %w{mynode othernode}.each do |node|
+ assert(interp.nodesearch_code(node), "Could not find %s" % node)
+ end
+ mynode = interp.nodesearch_code("mynode")
- classes.each do |tag|
- assert(scope.classlist.include?(tag), "Did not set class %s" % tag)
+ # Now try evaluating the node
+ assert_nothing_raised do
+ mynode.evaluate :scope => scope
end
- end
+ # Make sure that we can find each of the files
+ myfile = scope.findresource "file[/tmp/mynode]"
+ assert(myfile, "Could not find file from node")
+ assert_equal("root", myfile[:owner])
- # Test that we strip the domain off of host names before they are set as classes
- def test_nodenamestrip
- children = []
+ basefile = scope.findresource "file[/tmp/basenode]"
+ assert(basefile, "Could not find file from base node")
+ assert_equal("daemon", basefile[:owner])
- longname = "node.domain.com"
- children << nodedef(longname)
+ # Now make sure we can evaluate nodes with parents
+ child = interp.newnode(%w{child}, :parent => "basenode").shift
- # Create the wrapper object
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- scope = Puppet::Parser::Scope.new()
-
- assert_nothing_raised("Could not evaluate node") {
- objects = scope.evaluate(:names => [longname], :facts => {}, :ast => top)
- }
+ newscope = mkscope :interp => interp
+ assert_nothing_raised do
+ child.evaluate :scope => newscope
+ end
- assert(!scope.classlist.include?("node.domain.com"),
- "Node's long name got set")
- assert(scope.classlist.include?("node"), "Node's name did not get set")
+ assert(newscope.findresource("file[/tmp/base]"),
+ "Could not find base resource")
end
- # Make sure that deep class parentage works
- def test_classparentage
- children = []
- files = []
- base = classobj("base")
- files << "/base"
+ def test_collection
+ interp = mkinterp
+ scope = mkscope(:interp => interp)
- children << base
+ coll = nil
+ assert_nothing_raised do
+ coll = AST::Collection.new(:type => "file", :form => :virtual)
+ end
- parent = "base"
- 5.times { |i|
- name = "child%s" % i
- files << "/%s" % name
- children << classobj(name, :parentclass => nameobj(parent))
+ assert_instance_of(AST::Collection, coll)
- parent = name
- }
+ ret = nil
+ assert_nothing_raised do
+ ret = coll.evaluate :scope => scope
+ end
- children << functionobj("include", parent)
+ assert_instance_of(Puppet::Parser::Collector, ret)
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
+ # Now make sure we get it back from the scope
+ assert_equal([ret], scope.collections)
+ end
- objects = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
+ def test_virtual_collexp
+ @interp, @scope, @source = mkclassframing
- objects = objects.flatten
+ # make a resource
+ resource = mkresource(:type => "file", :title => "/tmp/testing",
+ :params => {:owner => "root", :group => "bin", :mode => "644"})
- files.each do |file|
- assert(objects.find { |o| o.name == file },
- "Could not find file %s" % file)
+ run_collection_queries(:virtual) do |string, result, query|
+ code = nil
+ assert_nothing_raised do
+ str, code = query.evaluate :scope => @scope
+ end
+
+ assert_instance_of(Proc, code)
+ assert_nothing_raised do
+ assert_equal(result, code.call(resource),
+ "'#{string}' failed")
+ end
end
end
- # Make sure we catch names that are specified like parameters.
- def test_name_or_param
- obj = nil
- assert_nothing_raised {
- obj = AST::ObjectDef.new(
- :type => nameobj("file"),
- :params => astarray(AST::ObjectParam.new(
- :param => stringobj("name"),
- :value => stringobj("yayness")
- ))
- )
- }
-
- scope = Puppet::Parser::Scope.new
+ if defined? ActiveRecord::Base
+ def test_exported_collexp
+ railsinit
+ Puppet[:storeconfigs] = true
+ @interp, @scope, @source = mkclassframing
+
+ # make a rails resource
+ railsresource "file", "/tmp/testing", :owner => "root", :group => "bin",
+ :mode => "644"
+
+ run_collection_queries(:exported) do |string, result, query|
+ code = nil
+ str = nil
+
+ # We don't support anything but the title in rails right now
+ retval = nil
+ bad = false
+ # Figure out if the search is for anything rails will ignore
+ string.scan(/(\w+) [!=]= \w+/) do |s|
+ unless s[0] == "title"
+ bad = true
+ break
+ end
+ end
- trans = nil
- assert_nothing_raised {
- trans = scope.evaluate(:ast => obj, :facts => {})
- }
+ # And if it is, make sure we throw an error.
+ if bad
+ assert_raise(Puppet::ParseError, "Evaluated '#{string}'") do
+ str, code = query.evaluate :scope => @scope
+ end
+ next
+ else
+ assert_nothing_raised("Could not evaluate '#{string}'") do
+ str, code = query.evaluate :scope => @scope
+ end
+ end
+ assert_nothing_raised("Could not find resource") do
+ retval = Puppet::Rails::RailsResource.find(:all,
+ :include => :rails_parameters,
+ :conditions => str)
+ end
- transobj = trans.shift
- assert(transobj.name, "Name did not convert from param to name")
- end
+ if result
+ assert_equal(1, retval.length, "Did not find resource with '#{string}'")
+ res = retval.shift
- if defined? ActiveRecord
- # Verify that our collection stuff works.
- def test_collection
- collectable = []
- non = []
- # First put some objects into the database.
- bucket = mk_transtree do |object, depth, width|
- # and mark some of them collectable
- if width % 2 == 1
- object.collectable = true
- collectable << object
+ assert_equal("file", res.restype)
+ assert_equal("/tmp/testing", res.title)
else
- non << object
+ assert_equal(0, retval.length, "found a resource with '#{string}'")
end
end
-
- # Now collect our facts
- facts = {}
- Facter.each do |fact, value| facts[fact] = value end
-
- assert_nothing_raised {
- Puppet::Rails.init
- }
-
- # Now try storing our crap
- assert_nothing_raised {
- host = Puppet::Rails::Host.store(
- :objects => bucket,
- :facts => facts,
- :host => facts["hostname"]
- )
- }
-
- # Now create an ast tree that collects that. They should all be files.
- coll = nil
- assert_nothing_raised {
- coll = AST::Collection.new(
- :type => nameobj("file")
- )
- }
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => [coll]
- )
- }
-
- objects = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top).flatten
- }
-
- assert(objects.length > 0, "Did not receive any collected objects")
end
- else
- $stderr.puts "No ActiveRecord -- skipping collection tests"
end
- # To fix #140. Currently non-functional.
- def disabled_test_classreuse
- children = []
-
- # Create the parent class, with a definition in it.
- children << classobj("parent", :code => AST::ASTArray.new(
- :file => __FILE__,
- :line => __LINE__,
- :children => [
- compobj("foo", :args => AST::ASTArray.new(
- :children => [nameobj("arg")]
- ),
- :code => AST::ASTArray.new(
- :file => __FILE__,
- :line => __LINE__,
- :children => [fileobj("/$arg")]
- )
- ),
- objectdef("foo", "ptest", {"arg" => "parentfoo"})
- ]
- ))
-
- # Create child class, also trying to use that definition
- children << classobj("child1", :parentclass => nameobj("parent"),
- :code => AST::ASTArray.new(
- :file => __FILE__,
- :line => __LINE__,
- :children => [
- objectdef("foo", "ctest", {"arg" => "childfoo"})
- ]
- )
- )
-
- # Call the parent first
- children << functionobj("include", "parent")
-
- # Then call the child, and make sure it can look up the definition
- children << functionobj("include", "child1")
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- objects = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
- end
-
- def test_if
- astif = nil
- astelse = nil
- fakeelse = FakeAST.new(:else)
- faketest = FakeAST.new(true)
- fakeif = FakeAST.new(:if)
-
- assert_nothing_raised {
- astelse = AST::Else.new(:statements => fakeelse)
- }
- assert_nothing_raised {
- astif = AST::IfStatement.new(
- :test => faketest,
- :statements => fakeif,
- :else => astelse
- )
- }
+ def run_collection_queries(form)
+ {true => [%{title == "/tmp/testing"}, %{(title == "/tmp/testing")},
+ %{title == "/tmp/testing" and group == bin}, %{title == bin or group == bin},
+ %{title == "/tmp/testing" or title == bin}, %{title == "/tmp/testing"},
+ %{(title == "/tmp/testing" or title == bin) and group == bin}],
+ false => [%{title == bin}, %{title == bin or (title == bin and group == bin)},
+ %{title != "/tmp/testing"}, %{title != "/tmp/testing" and group != bin}]
+ }.each do |res, ary|
+ ary.each do |str|
+ if form == :virtual
+ code = "File <| #{str} |>"
+ else
+ code = "File <<| #{str} |>>"
+ end
+ parser = mkparser
+ query = nil
- # We initialized it to true, so we should get that first
- ret = nil
- assert_nothing_raised {
- ret = astif.evaluate(:scope => "yay")
- }
- assert_equal(:if, ret)
+ assert_nothing_raised("Could not parse '#{str}'") do
+ query = parser.parse(code)[0].query
+ end
- # Now set it to false and check that
- faketest.evaluate = false
- assert_nothing_raised {
- ret = astif.evaluate(:scope => "yay")
- }
- assert_equal(:else, ret)
+ yield str, res, query
+ end
+ end
end
end
diff --git a/test/language/collector.rb b/test/language/collector.rb
new file mode 100755
index 000000000..d7ac059fa
--- /dev/null
+++ b/test/language/collector.rb
@@ -0,0 +1,206 @@
+#!/usr/bin/ruby
+
+require 'puppet/rails'
+require 'puppettest'
+require 'puppettest/parsertesting'
+require 'puppettest/resourcetesting'
+require 'puppettest/railstesting'
+
+class TestCollector < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
+ include PuppetTest::RailsTesting
+ Parser = Puppet::Parser
+ AST = Parser::AST
+
+ def setup
+ super
+ Puppet[:trace] = false
+ @interp, @scope, @source = mkclassframing
+ end
+
+ def test_virtual
+ # Make a virtual resource
+ virtual = mkresource(:type => "file", :title => "/tmp/virtual",
+ :virtual => true, :params => {:owner => "root"})
+ @scope.setresource virtual
+
+ # And a non-virtual
+ real = mkresource(:type => "file", :title => "/tmp/real",
+ :params => {:owner => "root"})
+ @scope.setresource real
+
+ # Now make a collector
+ coll = nil
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "file", nil, :virtual)
+ end
+
+ # Set it in our scope
+ @scope.newcollection(coll)
+
+ # Make sure it's in the collections
+ assert_equal([coll], @scope.collections)
+
+ # And try to collect the virtual resources.
+ ret = nil
+ assert_nothing_raised do
+ ret = coll.collect_virtual
+ end
+
+ assert_equal([virtual], ret)
+
+ # Now make sure evaluate does the right thing.
+ assert_nothing_raised do
+ ret = coll.evaluate
+ end
+
+ # Make sure it got deleted from the collection list
+ assert_equal([], @scope.collections)
+
+ # And make sure our virtual object is no longer virtual
+ assert(! virtual.virtual?, "Virtual object did not get realized")
+
+ # Now make a new collector of a different type and make sure it
+ # finds nothing.
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "exec", nil, :virtual)
+ end
+
+ # Remark this as virtual
+ virtual.virtual = true
+
+ assert_nothing_raised do
+ ret = coll.evaluate
+ end
+
+ assert_equal([], ret)
+ end
+
+ if defined? ActiveRecord::Base
+ def test_collect_exported
+ railsinit
+ # make an exported resource
+ exported = mkresource(:type => "file", :title => "/tmp/exported",
+ :exported => true, :params => {:owner => "root"})
+ @scope.setresource exported
+
+ assert(exported.exported?, "Object was not marked exported")
+ assert(exported.virtual?, "Object was not marked virtual")
+
+ # And a non-exported
+ real = mkresource(:type => "file", :title => "/tmp/real",
+ :params => {:owner => "root"})
+ @scope.setresource real
+
+ # Now make a collector
+ coll = nil
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "file", nil, :exported)
+ end
+
+ # Set it in our scope
+ @scope.newcollection(coll)
+
+ # Make sure it's in the collections
+ assert_equal([coll], @scope.collections)
+
+ # And try to collect the virtual resources.
+ ret = nil
+ assert_nothing_raised do
+ ret = coll.collect_exported
+ end
+
+ assert_equal([exported], ret)
+
+ # Now make sure evaluate does the right thing.
+ assert_nothing_raised do
+ ret = coll.evaluate
+ end
+
+ # Make sure it got deleted from the collection list
+ assert_equal([], @scope.collections)
+
+ # And make sure our exported object is no longer exported
+ assert(! exported.virtual?, "Virtual object did not get realized")
+
+ # But it should still be marked exported.
+ assert(exported.exported?, "Resource got un-exported")
+
+ # Now make a new collector of a different type and make sure it
+ # finds nothing.
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "exec", nil, :exported)
+ end
+
+ # Remark this as virtual
+ exported.virtual = true
+
+ assert_nothing_raised do
+ ret = coll.evaluate
+ end
+
+ assert_equal([], ret)
+ end
+
+ def test_collection_conflicts
+ railsinit
+
+ # First make a railshost we can conflict with
+ host = Puppet::Rails::Host.new(:name => "myhost")
+
+ host.rails_resources.build(:title => "/tmp/conflicttest", :restype => "file",
+ :exported => true)
+
+ host.save
+
+ # Now make a normal resource
+ normal = mkresource(:type => "file", :title => "/tmp/conflicttest",
+ :params => {:owner => "root"})
+ @scope.setresource normal
+
+ # Now make a collector
+ coll = nil
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "file", nil, :exported)
+ end
+
+ # And try to collect the virtual resources.
+ assert_raise(Puppet::ParseError) do
+ ret = coll.collect_exported
+ end
+ end
+
+ # Make sure we do not collect resources from the host we're on
+ def test_no_resources_from_me
+ railsinit
+
+ # Make our configuration
+ host = Puppet::Rails::Host.new(:name => "myhost")
+
+ host.rails_resources.build(:title => "/tmp/hosttest", :restype => "file",
+ :exported => true)
+
+ host.save
+
+ @scope.host = "myhost"
+
+ # Now make a collector
+ coll = nil
+ assert_nothing_raised do
+ coll = Puppet::Parser::Collector.new(@scope, "file", nil, :exported)
+ end
+
+ # And make sure we get nada back
+ ret = nil
+ assert_nothing_raised do
+ ret = coll.collect_exported
+ end
+
+ assert(ret.empty?, "Found exports from our own host")
+ end
+ end
+end
+
+# $Id$
diff --git a/test/language/functions.rb b/test/language/functions.rb
index 2c3246e6a..062971bf1 100755
--- a/test/language/functions.rb
+++ b/test/language/functions.rb
@@ -35,7 +35,7 @@ class TestLangFunctions < Test::Unit::TestCase
)
end
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
val = nil
assert_nothing_raised do
val = func.evaluate(:scope => scope)
@@ -45,10 +45,10 @@ class TestLangFunctions < Test::Unit::TestCase
end
def test_taggedfunction
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
tag = "yayness"
- scope.setclass(tag.object_id, tag)
+ scope.tag(tag)
{"yayness" => true, "booness" => false}.each do |tag, retval|
func = taggedobj(tag, :rvalue)
@@ -75,7 +75,7 @@ class TestLangFunctions < Test::Unit::TestCase
)
end
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
val = nil
assert_raise(Puppet::ParseError) do
val = func.evaluate(:scope => scope)
@@ -107,7 +107,7 @@ class TestLangFunctions < Test::Unit::TestCase
end
ast = varobj("output", func)
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
assert_raise(Puppet::ParseError) do
ast.evaluate(:scope => scope)
end
@@ -145,7 +145,7 @@ class TestLangFunctions < Test::Unit::TestCase
end
ast = varobj("output", func)
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
assert_raise(Puppet::ParseError) do
ast.evaluate(:scope => scope)
end
@@ -180,7 +180,7 @@ class TestLangFunctions < Test::Unit::TestCase
end
ast = varobj("output", func)
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
scope.setvar("myvar", "this is yayness")
assert_raise(Puppet::ParseError) do
ast.evaluate(:scope => scope)
@@ -257,7 +257,7 @@ class TestLangFunctions < Test::Unit::TestCase
"" => "",
false => "false",
}.each do |string, value|
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
assert_raise(Puppet::ParseError) do
ast.evaluate(:scope => scope)
end
diff --git a/test/language/interpreter.rb b/test/language/interpreter.rb
index c127ab517..e195e28b3 100755
--- a/test/language/interpreter.rb
+++ b/test/language/interpreter.rb
@@ -8,10 +8,18 @@ require 'puppet/parser/parser'
require 'puppet/client'
require 'puppet/rails'
require 'puppettest'
+require 'puppettest/resourcetesting'
+require 'puppettest/parsertesting'
+require 'puppettest/servertest'
+require 'puppettest/railstesting'
+require 'timeout'
class TestInterpreter < Test::Unit::TestCase
include PuppetTest
include PuppetTest::ServerTest
+ include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
+ include PuppetTest::RailsTesting
AST = Puppet::Parser::AST
# create a simple manifest that uses nodes to create a file
@@ -135,81 +143,48 @@ class TestInterpreter < Test::Unit::TestCase
return parent, classes
end
- def test_ldapnodes
+ def test_ldapsearch
Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com"
Puppet[:ldapnodes] = true
ldapconnect()
- file = tempfile()
- files = []
- parentfile = tempfile() + "-parent"
- files << parentfile
- hostname = Facter["hostname"].value
- lparent, lclasses = ldaphost(Facter["hostname"].value)
- assert(lclasses, "Did not retrieve info from ldap")
- File.open(file, "w") { |f|
- f.puts "node #{lparent} {
- file { \"#{parentfile}\": ensure => file }
-}"
-
- lclasses.each { |klass|
- kfile = tempfile() + "-klass"
- files << kfile
- f.puts "class #{klass} { file { \"#{kfile}\": ensure => file } }"
- }
- }
- interp = nil
- assert_nothing_raised {
- interp = Puppet::Parser::Interpreter.new(
- :Manifest => file
- )
- }
- parent = nil
- classes = nil
- # First make sure we get the default node for unknown hosts
- dparent, dclasses = ldaphost("default")
+ interp = mkinterp :NodeSources => [:ldap, :code]
- assert_nothing_raised {
- parent, classes = interp.nodesearch("nosuchhostokay")
- }
+ # Make sure we can find 'culain' in ldap
+ parent, classes = nil
+ assert_nothing_raised do
+ parent, classes = interp.ldapsearch("culain")
+ end
- assert_equal(dparent, parent, "Default parent node did not match")
- assert_equal(dclasses, classes, "Default parent class list did not match")
+ realparent, realclasses = ldaphost("culain")
+ assert_equal(realparent, parent)
+ assert_equal(realclasses, classes)
+ end
- # Look for a host we know doesn't have a parent
- npparent, npclasses = ldaphost("noparent")
- assert_nothing_raised {
- #parent, classes = interp.nodesearch_ldap("noparent")
- parent, classes = interp.nodesearch("noparent")
- }
+ def test_ldapnodes
+ Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com"
+ Puppet[:ldapnodes] = true
- assert_equal(npparent, parent, "Parent node did not match")
- assert_equal(npclasses, classes, "Class list did not match")
+ ldapconnect()
- # Now look for our normal host
- assert_nothing_raised {
- parent, classes = interp.nodesearch_ldap(hostname)
- }
+ interp = mkinterp :NodeSources => [:ldap, :code]
- assert_equal(lparent, parent, "Parent node did not match")
- assert_equal(lclasses, classes, "Class list did not match")
+ # culain uses basenode, so create that
+ basenode = interp.newnode([:basenode])[0]
- objects = nil
- assert_nothing_raised {
- objects = interp.run(hostname, Puppet::Client::MasterClient.facts)
- }
+ # Make sure we can find 'culain' in ldap
+ culain = nil
+ assert_nothing_raised do
+ culain = interp.nodesearch_ldap("culain")
+ end
- comp = nil
- assert_nothing_raised {
- comp = objects.to_type
- }
+ assert(culain, "Did not find culain in ldap")
- assert_apply(comp)
- files.each { |cfile|
- @@tmpfiles << cfile
- assert(FileTest.exists?(cfile), "Did not make %s" % cfile)
- }
+ assert_nothing_raised do
+ assert_equal(basenode.fqname.to_s, culain.parentclass.fqname.to_s,
+ "Did not get parent class")
+ end
end
if Puppet::SUIDManager.uid == 0 and Facter["hostname"].value == "culain"
@@ -260,87 +235,32 @@ class TestInterpreter < Test::Unit::TestCase
# Make sure searchnode behaves as we expect.
def test_nodesearch
- # First create a fake nodesearch algorithm
- i = 0
- bucket = []
- Puppet::Parser::Interpreter.send(:define_method, "nodesearch_fake") do |node|
- return nil, nil if node == "default"
-
- return bucket[0], bucket[1]
- end
- text = %{
-node nodeparent {}
-node othernodeparent {}
-class nodeclass {}
-class nothernode {}
-}
- manifest = tempfile()
- File.open(manifest, "w") do |f| f.puts text end
- interp = nil
- assert_nothing_raised {
- interp = Puppet::Parser::Interpreter.new(
- :Manifest => manifest,
- :NodeSources => [:fake]
- )
- }
- # Make sure it behaves correctly for all forms
- [[nil, nil],
- ["nodeparent", nil],
- [nil, ["nodeclass"]],
- [nil, ["nodeclass", "nothernode"]],
- ["othernodeparent", ["nodeclass", "nothernode"]],].each do |ary|
- # Set the return values
- bucket = ary
-
- # Look them back up
- parent, classes = interp.nodesearch("mynode")
-
- # Basically, just make sure that if we have either or both,
- # we get a result back.
- assert_equal(ary[0], parent,
- "Parent is not %s" % parent)
- assert_equal(ary[1], classes,
- "Parent is not %s" % parent)
-
- next if ary == [nil, nil]
- # Now make sure we actually get the configuration. This will throw
- # an exception if we don't.
- assert_nothing_raised do
- interp.run("mynode", {})
- end
- end
- end
-
- # Make sure nodesearch uses all names, not just one.
- def test_nodesearch_multiple_names
- bucket = {}
- Puppet::Parser::Interpreter.send(:define_method, "nodesearch_multifake") do |node|
- if bucket[node]
- return *bucket[node]
- else
- return nil, nil
- end
+ interp = mkinterp
+
+ # Make some nodes
+ names = %w{node1 node2 node2.domain.com}
+ interp.newnode names
+
+ nodes = {}
+ # Make sure we can find them all, using the direct method
+ names.each do |name|
+ nodes[name] = interp.nodesearch_code(name)
+ assert(nodes[name], "Could not find %s" % name)
+ nodes[name].file = __FILE__
end
- manifest = tempfile()
- File.open(manifest, "w") do |f| f.puts "" end
- interp = nil
- assert_nothing_raised {
- interp = Puppet::Parser::Interpreter.new(
- :Manifest => manifest,
- :NodeSources => [:multifake]
- )
- }
- bucket["name.domain.com"] = [:parent, [:classes]]
-
- ret = nil
-
- assert_nothing_raised do
- assert_equal bucket["name.domain.com"],
- interp.nodesearch("name", "name.domain.com")
+ # Now let's try it with the nodesearch method
+ names.each do |name|
+ node = interp.nodesearch(name)
+ assert(node, "Could not find #{name} via nodesearch")
end
+ # Now make sure the longest match always wins
+ node = interp.nodesearch(*%w{node2 node2.domain.com})
+ assert(node, "Did not find node2")
+ assert_equal("node2.domain.com", node.fqname,
+ "Did not get longest match")
end
def test_parsedate
@@ -389,4 +309,519 @@ class nothernode {}
newdate = interp.parsedate
assert(date != newdate, "Parsedate was not updated")
end
+
+ # Make sure our node gets added to the node table.
+ def test_newnode
+ interp = mkinterp
+
+ # First just try calling it directly
+ assert_nothing_raised {
+ interp.newnode("mynode", :code => :yay)
+ }
+
+ assert_equal(:yay, interp.nodesearch_code("mynode").code)
+
+ # Now make sure that trying to redefine it throws an error.
+ assert_raise(Puppet::ParseError) {
+ interp.newnode("mynode", {})
+ }
+
+ # Now try one with no code
+ assert_nothing_raised {
+ interp.newnode("simplenode", :parent => :foo)
+ }
+
+ # Make sure trying to get the parentclass throws an error
+ assert_raise(Puppet::ParseError) do
+ interp.nodesearch_code("simplenode").parentclass
+ end
+
+ # Now define the parent node
+ interp.newnode(:foo)
+
+ # And make sure we get things back correctly
+ assert_equal("foo", interp.nodesearch_code("simplenode").parentclass.fqname)
+ assert_nil(interp.nodesearch_code("simplenode").code)
+
+ # Now make sure that trying to redefine it throws an error.
+ assert_raise(Puppet::ParseError) {
+ interp.newnode("mynode", {})
+ }
+
+ # Test multiple names
+ names = ["one", "two", "three"]
+ assert_nothing_raised {
+ interp.newnode(names, {:code => :yay, :parent => :foo})
+ }
+
+ names.each do |name|
+ assert_equal(:yay, interp.nodesearch_code(name).code)
+ assert_equal("foo", interp.nodesearch_code(name).parentclass.name)
+ # Now make sure that trying to redefine it throws an error.
+ assert_raise(Puppet::ParseError) {
+ interp.newnode(name, {})
+ }
+ end
+ end
+
+ # Make sure we're correctly generating a node definition.
+ def test_gennode
+ interp = mkinterp
+
+ node = nil
+ assert_nothing_raised {
+ node = interp.gennode("nodeA", :classes => %w{classA classB})
+ }
+
+ assert_instance_of(Puppet::Parser::AST::Node, node)
+
+ assert_equal("nodeA", node.name)
+ end
+
+ def test_fqfind
+ interp = mkinterp
+
+ table = {}
+ # Define a bunch of things.
+ %w{a c a::b a::b::c a::c a::b::c::d a::b::c::d::e::f c::d}.each do |string|
+ table[string] = string
+ end
+
+ check = proc do |namespace, hash|
+ hash.each do |thing, result|
+ assert_equal(result, interp.fqfind(namespace, thing, table),
+ "Could not find %s in %s" % [thing, namespace])
+ end
+ end
+
+ # Now let's do some test lookups.
+
+ # First do something really simple
+ check.call "a", "b" => "a::b", "b::c" => "a::b::c", "d" => nil, "::c" => "c"
+
+ check.call "a::b", "c" => "a::b::c", "b" => "a::b", "a" => "a"
+
+ check.call "a::b::c::d::e", "c" => "a::b::c", "::c" => "c",
+ "c::d" => "a::b::c::d", "::c::d" => "c::d"
+
+ check.call "", "a" => "a", "a::c" => "a::c"
+ end
+
+ def test_newdefine
+ interp = mkinterp
+
+ assert_nothing_raised {
+ interp.newdefine("mydefine", :code => :yay,
+ :arguments => ["a", stringobj("b")])
+ }
+
+ mydefine = interp.finddefine("", "mydefine")
+ assert(mydefine, "Could not find definition")
+ assert_equal("mydefine", interp.finddefine("", "mydefine").type)
+ assert_equal("", mydefine.namespace)
+ assert_equal("mydefine", mydefine.type)
+
+ assert_raise(Puppet::ParseError) do
+ interp.newdefine("mydefine", :code => :yay,
+ :arguments => ["a", stringobj("b")])
+ end
+
+ # Now define the same thing in a different scope
+ assert_nothing_raised {
+ interp.newdefine("other::mydefine", :code => :other,
+ :arguments => ["a", stringobj("b")])
+ }
+ other = interp.finddefine("other", "mydefine")
+ assert(other, "Could not find definition")
+ assert(interp.finddefine("", "other::mydefine"),
+ "Could not find other::mydefine")
+ assert_equal(:other, other.code)
+ assert_equal("other", other.namespace)
+ assert_equal("mydefine", other.type)
+ assert_equal("other::mydefine", other.fqname)
+ end
+
+ def test_newclass
+ interp = mkinterp
+
+ mkcode = proc do |ary|
+ classes = ary.collect do |string|
+ AST::FlatString.new(:value => string)
+ end
+ AST::ASTArray.new(:children => classes)
+ end
+ scope = Puppet::Parser::Scope.new(:interp => interp)
+
+ # First make sure that code is being appended
+ code = mkcode.call(%w{original code})
+
+ klass = nil
+ assert_nothing_raised {
+ klass = interp.newclass("myclass", :code => code)
+ }
+
+ assert(klass, "Did not return class")
+
+ assert(interp.findclass("", "myclass"), "Could not find definition")
+ assert_equal("myclass", interp.findclass("", "myclass").type)
+ assert_equal(%w{original code},
+ interp.findclass("", "myclass").code.evaluate(:scope => scope))
+
+ # Now create the same class name in a different scope
+ assert_nothing_raised {
+ klass = interp.newclass("other::myclass",
+ :code => mkcode.call(%w{something diff}))
+ }
+ assert(klass, "Did not return class")
+ other = interp.findclass("other", "myclass")
+ assert(other, "Could not find class")
+ assert(interp.findclass("", "other::myclass"), "Could not find class")
+ assert_equal("other::myclass", other.fqname)
+ assert_equal("other", other.namespace)
+ assert_equal("myclass", other.type)
+ assert_equal(%w{something diff},
+ interp.findclass("other", "myclass").code.evaluate(:scope => scope))
+
+ # Newclass behaves differently than the others -- it just appends
+ # the code to the existing class.
+ code = mkcode.call(%w{something new})
+ assert_nothing_raised do
+ klass = interp.newclass("myclass", :code => code)
+ end
+ assert(klass, "Did not return class when appending")
+ assert_equal(%w{original code something new},
+ interp.findclass("", "myclass").code.evaluate(:scope => scope))
+
+ # Make sure newclass deals correctly with nodes with no code
+ klass = interp.newclass("nocode")
+ assert(klass, "Did not return class")
+
+ assert_nothing_raised do
+ klass = interp.newclass("nocode", :code => mkcode.call(%w{yay test}))
+ end
+ assert(klass, "Did not return class with no code")
+ assert_equal(%w{yay test},
+ interp.findclass("", "nocode").code.evaluate(:scope => scope))
+
+ # Then try merging something into nothing
+ interp.newclass("nocode2", :code => mkcode.call(%w{foo test}))
+ assert(klass, "Did not return class with no code")
+
+ assert_nothing_raised do
+ klass = interp.newclass("nocode2")
+ end
+ assert(klass, "Did not return class with no code")
+ assert_equal(%w{foo test},
+ interp.findclass("", "nocode2").code.evaluate(:scope => scope))
+
+ # And lastly, nothing and nothing
+ klass = interp.newclass("nocode3")
+ assert(klass, "Did not return class with no code")
+
+ assert_nothing_raised do
+ klass = interp.newclass("nocode3")
+ end
+ assert(klass, "Did not return class with no code")
+ assert_nil(interp.findclass("", "nocode3").code)
+
+ end
+
+ # Now make sure we get appropriate behaviour with parent class conflicts.
+ def test_newclass_parentage
+ interp = mkinterp
+ interp.newclass("base1")
+ interp.newclass("one::two::three")
+
+ # First create it with no parentclass.
+ assert_nothing_raised {
+ interp.newclass("sub")
+ }
+ assert(interp.findclass("", "sub"), "Could not find definition")
+ assert_nil(interp.findclass("", "sub").parentclass)
+
+ # Make sure we can't set the parent class to ourself.
+ assert_raise(Puppet::ParseError) {
+ interp.newclass("sub", :parent => "sub")
+ }
+
+ # Now create another one, with a parentclass.
+ assert_nothing_raised {
+ interp.newclass("sub", :parent => "base1")
+ }
+
+ # Make sure we get the right parent class, and make sure it's an object.
+ assert_equal(interp.findclass("", "base1"),
+ interp.findclass("", "sub").parentclass)
+
+ # Now make sure we get a failure if we try to conflict.
+ assert_raise(Puppet::ParseError) {
+ interp.newclass("sub", :parent => "one::two::three")
+ }
+
+ # Make sure that failure didn't screw us up in any way.
+ assert_equal(interp.findclass("", "base1"),
+ interp.findclass("", "sub").parentclass)
+ # But make sure we can create a class with a fq parent
+ assert_nothing_raised {
+ interp.newclass("another", :parent => "one::two::three")
+ }
+ assert_equal(interp.findclass("", "one::two::three"),
+ interp.findclass("", "another").parentclass)
+
+ end
+
+ def test_namesplit
+ interp = mkinterp
+
+ assert_nothing_raised do
+ {"base::sub" => %w{base sub},
+ "main" => ["", "main"],
+ "one::two::three::four" => ["one::two::three", "four"],
+ }.each do |name, ary|
+ result = interp.namesplit(name)
+ assert_equal(ary, result, "%s split to %s" % [name, result])
+ end
+ end
+ end
+
+ # Make sure you can't have classes and defines with the same name in the
+ # same scope.
+ def test_classes_beat_defines
+ interp = mkinterp
+
+ assert_nothing_raised {
+ interp.newclass("yay::funtest")
+ }
+
+ assert_raise(Puppet::ParseError) do
+ interp.newdefine("yay::funtest")
+ end
+
+ assert_nothing_raised {
+ interp.newdefine("yay::yaytest")
+ }
+
+ assert_raise(Puppet::ParseError) do
+ interp.newclass("yay::yaytest")
+ end
+ end
+
+ # Make sure our whole chain works.
+ def test_evaluate
+ interp, scope, source = mkclassframing
+
+ # Create a define that we'll be using
+ interp.newdefine("wrapper", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", varref("name"), "owner" => "root")
+ ]))
+
+ # Now create a resource that uses that define
+ define = mkresource(:type => "wrapper", :title => "/tmp/testing",
+ :scope => scope, :source => source, :params => :none)
+
+ scope.setresource define
+
+ # And a normal resource
+ scope.setresource mkresource(:type => "file", :title => "/tmp/rahness",
+ :scope => scope, :source => source,
+ :params => {:owner => "root"})
+
+ # Now evaluate everything
+ objects = nil
+ interp.usenodes = false
+ assert_nothing_raised do
+ objects = interp.evaluate(nil, {})
+ end
+
+ assert_instance_of(Puppet::TransBucket, objects)
+ end
+
+ def test_evaliterate
+ interp, scope, source = mkclassframing
+
+ # Create a top-level definition that creates a builtin object
+ interp.newdefine("one", :arguments => [%w{owner}],
+ :code => AST::ASTArray.new(:children => [
+ resourcedef("file", varref("name"),
+ "owner" => varref("owner")
+ )
+ ])
+ )
+
+ # Create another definition to call that one
+ interp.newdefine("two", :arguments => [%w{owner}],
+ :code => AST::ASTArray.new(:children => [
+ resourcedef("one", varref("name"),
+ "owner" => varref("owner")
+ )
+ ])
+ )
+
+ # And then a third
+ interp.newdefine("three", :arguments => [%w{owner}],
+ :code => AST::ASTArray.new(:children => [
+ resourcedef("two", varref("name"),
+ "owner" => varref("owner")
+ )
+ ])
+ )
+
+ three = Puppet::Parser::Resource.new(
+ :type => "three", :title => "/tmp/yayness",
+ :scope => scope, :source => source,
+ :params => paramify(source, :owner => "root")
+ )
+
+ scope.setresource(three)
+
+ ret = nil
+ assert_nothing_raised do
+ ret = scope.unevaluated
+ end
+
+
+ assert_instance_of(Array, ret)
+ assert(1, ret.length)
+ assert_equal([three], ret)
+
+ assert(ret.detect { |r| r.ref == "three[/tmp/yayness]"},
+ "Did not get three back as unevaluated")
+
+ # Now translate the whole tree
+ assert_nothing_raised do
+ interp.evaliterate(scope)
+ end
+
+ # Now make sure we've got our file
+ file = scope.findresource "file[/tmp/yayness]"
+ assert(file, "Could not find file")
+
+ assert_equal("root", file[:owner])
+ end
+
+ # Make sure we fail if there are any leftover overrides to perform.
+ # This would normally mean that someone is trying to override an object
+ # that does not exist.
+ def test_failonleftovers
+ interp, scope, source = mkclassframing
+
+ # Make sure we don't fail, since there are no overrides
+ assert_nothing_raised do
+ interp.failonleftovers(scope)
+ end
+
+ # Add an override, and make sure it causes a failure
+ over1 = mkresource :scope => scope, :source => source,
+ :params => {:one => "yay"}
+
+ scope.setoverride(over1)
+
+ assert_raise(Puppet::ParseError) do
+ interp.failonleftovers(scope)
+ end
+
+ end
+
+ def test_evalnode
+ interp = mkinterp
+ interp.usenodes = false
+ scope = Parser::Scope.new(:interp => interp)
+ facts = Facter.to_hash
+
+ # First make sure we get no failures when client is nil
+ assert_nothing_raised do
+ interp.evalnode(nil, scope, facts)
+ end
+
+ # Now define a node
+ interp.newnode "mynode", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/testing", "owner" => "root")
+ ])
+
+ # Eval again, and make sure it does nothing
+ assert_nothing_raised do
+ interp.evalnode("mynode", scope, facts)
+ end
+
+ assert_nil(scope.findresource("file[/tmp/testing]"),
+ "Eval'ed node with nodes off")
+
+ # Now enable usenodes and make sure it works.
+ interp.usenodes = true
+ assert_nothing_raised do
+ interp.evalnode("mynode", scope, facts)
+ end
+ file = scope.findresource("file[/tmp/testing]")
+
+ assert_instance_of(Puppet::Parser::Resource, file,
+ "Could not find file")
+ end
+
+ # This is mostly used for the cfengine module
+ def test_specificclasses
+ interp = mkinterp :Classes => %w{klass1 klass2}, :UseNodes => false
+
+ # Make sure it's not a failure to be missing classes, since
+ # we're using the cfengine class list, which is huge.
+ assert_nothing_raised do
+ interp.evaluate(nil, {})
+ end
+
+ interp.newclass("klass1", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/klass1", "owner" => "root")
+ ]))
+ interp.newclass("klass2", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/klass2", "owner" => "root")
+ ]))
+
+ ret = nil
+ assert_nothing_raised do
+ ret = interp.evaluate(nil, {})
+ end
+
+ found = ret.flatten.collect do |res| res.name end
+
+ assert(found.include?("/tmp/klass1"), "Did not evaluate klass1")
+ assert(found.include?("/tmp/klass2"), "Did not evaluate klass2")
+ end
+
+ if defined? ActiveRecord::Base
+ # We need to make sure finished objects are stored in the db.
+ def test_finish_before_store
+ railsinit
+ interp = mkinterp
+
+ node = interp.newnode ["myhost"], :code => AST::ASTArray.new(:children => [
+ resourcedef("file", "/tmp/yay", :group => "root"),
+ defaultobj("file", :owner => "root")
+ ])
+
+ interp.newclass "myclass", :code => AST::ASTArray.new(:children => [
+ ])
+
+ interp.newclass "sub", :parent => "myclass",
+ :code => AST::ASTArray.new(:children => [
+ resourceoverride("file", "/tmp/yay", :owner => "root")
+ ]
+ )
+
+ # Now do the rails crap
+ Puppet[:storeconfigs] = true
+
+ interp.evaluate("myhost", {})
+
+ # And then retrieve the object from rails
+ res = Puppet::Rails::RailsResource.find_by_restype_and_title("file", "/tmp/yay")
+
+ assert(res, "Did not get resource from rails")
+
+ param = res.rails_parameters.find_by_name("owner")
+
+ assert(param, "Did not find owner param")
+
+ assert_equal("root", param[:value])
+ end
+ end
end
+
+# $Id$
diff --git a/test/language/lexer.rb b/test/language/lexer.rb
index 496087ba2..26b85b3b7 100644
--- a/test/language/lexer.rb
+++ b/test/language/lexer.rb
@@ -13,6 +13,10 @@ class TestLexer < Test::Unit::TestCase
include PuppetTest
def setup
super
+ mklexer
+ end
+
+ def mklexer
@lexer = Puppet::Parser::Lexer.new()
end
@@ -79,9 +83,10 @@ class TestLexer < Test::Unit::TestCase
def test_files
textfiles() { |file|
- @lexer.file = file
+ lexer = Puppet::Parser::Lexer.new()
+ lexer.file = file
assert_nothing_raised() {
- @lexer.fullscan()
+ lexer.fullscan()
}
Puppet::Type.allclear
}
@@ -149,6 +154,75 @@ class TestLexer < Test::Unit::TestCase
assert_equal([[:AT, "@"], [:NAME, "type"], [:LBRACE, "{"], [false,false]],ret)
end
+
+ def test_namespace
+ @lexer.string = %{class myclass}
+
+ assert_nothing_raised {
+ @lexer.fullscan
+ }
+
+ assert_equal("myclass", @lexer.namespace)
+
+ assert_nothing_raised do
+ @lexer.namepop
+ end
+
+ assert_equal("", @lexer.namespace)
+
+ @lexer.string = "class base { class sub { class more"
+
+ assert_nothing_raised {
+ @lexer.fullscan
+ }
+
+ assert_equal("base::sub::more", @lexer.namespace)
+
+ assert_nothing_raised do
+ @lexer.namepop
+ end
+
+ assert_equal("base::sub", @lexer.namespace)
+
+ # Now try it with some fq names
+ mklexer
+
+ @lexer.string = "class base { class sub::more {"
+
+ assert_nothing_raised {
+ @lexer.fullscan
+ }
+
+ assert_equal("base::sub::more", @lexer.namespace)
+
+ assert_nothing_raised do
+ @lexer.namepop
+ end
+
+ assert_equal("base", @lexer.namespace)
+ end
+
+ def test_indefine
+ @lexer.string = %{define me}
+
+ assert_nothing_raised {
+ @lexer.fullscan
+ }
+
+ assert(@lexer.indefine?, "Lexer not considered in define")
+
+ # Now make sure we throw an error when trying to nest defines.
+ assert_raise(Puppet::ParseError) do
+ @lexer.string = %{define another}
+ @lexer.fullscan
+ end
+
+ assert_nothing_raised do
+ @lexer.indefine = false
+ end
+
+ assert(! @lexer.indefine?, "Lexer still considered in define")
+ end
end
# $Id$
diff --git a/test/language/node.rb b/test/language/node.rb
index 791c44874..251e4c4aa 100644
--- a/test/language/node.rb
+++ b/test/language/node.rb
@@ -8,7 +8,6 @@ class TestParser < Test::Unit::TestCase
def setup
super
Puppet[:parseonly] = true
- @parser = Puppet::Parser::Parser.new()
end
def test_simple_hostname
@@ -41,48 +40,40 @@ class TestParser < Test::Unit::TestCase
unless hostnames.is_a?(Array)
hostnames = [ hostnames ]
end
+ interp = nil
assert_nothing_raised {
- @parser.string = "node #{hostnames.join(", ")} { }"
+ interp = mkinterp :Code => "node #{hostnames.join(", ")} { }"
}
# Strip quotes
hostnames.map! { |s| s.sub(/^'(.*)'$/, "\\1") }
- ast = nil
+
+ # parse
assert_nothing_raised {
- ast = @parser.parse
+ interp.send(:parsefiles)
}
- # Verify that the AST has the expected structure
- # and that the leaves have the right hostnames in them
- assert_kind_of(AST::ASTArray, ast)
- assert_equal(1, ast.children.size)
- nodedef = ast.children[0]
- assert_kind_of(AST::NodeDef, nodedef)
- assert_kind_of(AST::ASTArray, nodedef.names)
- assert_equal(hostnames.size, nodedef.names.children.size)
- hostnames.size.times do |i|
- hostnode = nodedef.names.children[i]
- assert_kind_of(AST::HostName, hostnode)
- assert_equal(hostnames[i], hostnode.value)
+
+ # Now make sure we can look up each of the names
+ hostnames.each do |name|
+ assert(interp.nodesearch_code(name),
+ "Could not find node %s" % name)
end
end
def check_nonparseable(hostname)
- assert_nothing_raised {
- @parser.string = "node #{hostname} { }"
- }
-
- assert_raise(Puppet::DevError, Puppet::ParseError) {
- @parser.parse
+ interp = nil
+ assert_raise(Puppet::DevError, Puppet::ParseError, "#{hostname} passed") {
+ interp = mkinterp :Code => "node #{hostname} { }"
+ interp.send(:parsefiles)
}
end
# Make sure we can find default nodes if there's no other entry
def test_default_node
Puppet[:parseonly] = false
- @parser = Puppet::Parser::Parser.new()
fileA = tempfile()
fileB = tempfile()
- @parser.string = %{
+ code = %{
node mynode {
file { "#{fileA}": ensure => file }
}
@@ -91,34 +82,25 @@ node default {
file { "#{fileB}": ensure => file }
}
}
-
- # First make sure it parses
- ast = nil
+ interp = nil
assert_nothing_raised {
- ast = @parser.parse
- }
-
- args = {
- :ast => ast,
- :facts => {},
- :names => ["mynode"]
+ interp = mkinterp :Code => code
}
- # Make sure we get a config for "mynode"
- trans = nil
+ # First make sure it parses
assert_nothing_raised {
- trans = Puppet::Parser::Scope.new.evaluate(args)
+ interp.send(:parsefiles)
}
- assert(trans, "Did not get config for mynode")
+ # Make sure we find our normal node
+ assert(interp.nodesearch("mynode"),
+ "Did not find normal node")
- args[:names] = ["othernode"]
- # Now make sure the default node is used
- trans = nil
- assert_nothing_raised {
- trans = Puppet::Parser::Scope.new.evaluate(args)
- }
+ # Now look for the default node
+ default = interp.nodesearch("someother")
+ assert(default,
+ "Did not find default node")
- assert(trans, "Did not get config for default node")
+ assert_equal("default", default.fqname)
end
end
diff --git a/test/language/parser.rb b/test/language/parser.rb
index d6e176870..dbe48616a 100644
--- a/test/language/parser.rb
+++ b/test/language/parser.rb
@@ -8,15 +8,15 @@ class TestParser < Test::Unit::TestCase
super
Puppet[:parseonly] = true
#@lexer = Puppet::Parser::Lexer.new()
- @parser = Puppet::Parser::Parser.new()
end
def test_each_file
textfiles { |file|
+ parser = mkparser
Puppet.debug("parsing %s" % file) if __FILE__ == $0
assert_nothing_raised() {
- @parser.file = file
- @parser.parse
+ parser.file = file
+ parser.parse
}
Puppet::Type.eachtype { |type|
@@ -32,18 +32,20 @@ class TestParser < Test::Unit::TestCase
def test_failers
failers { |file|
+ parser = mkparser
Puppet.debug("parsing failer %s" % file) if __FILE__ == $0
assert_raise(Puppet::ParseError) {
- @parser.file = file
- ast = @parser.parse
- Puppet::Parser::Scope.new.evaluate(:ast => ast)
+ parser.file = file
+ ast = parser.parse
+ scope = mkscope :interp => parser.interp
+ ast.evaluate :scope => scope
}
Puppet::Type.allclear
}
end
def test_arrayrvalues
- parser = Puppet::Parser::Parser.new()
+ parser = mkparser
ret = nil
file = tempfile()
assert_nothing_raised {
@@ -83,7 +85,7 @@ class TestParser < Test::Unit::TestCase
}
assert_nothing_raised("Could not parse multiple files") {
- parser = Puppet::Parser::Parser.new()
+ parser = mkparser
parser.file = manifest
parser.parse
}
@@ -98,103 +100,18 @@ class TestParser < Test::Unit::TestCase
f.puts "import \" no such file \""
end
assert_raise(Puppet::ParseError) {
- parser = Puppet::Parser::Parser.new()
+ parser = mkparser
parser.file = manifest
parser.parse
}
end
- def test_defaults
- basedir = File.join(tmpdir(), "defaulttesting")
- @@tmpfiles << basedir
- Dir.mkdir(basedir)
-
- defs1 = {
- "testing" => "value"
- }
-
- defs2 = {
- "one" => "two",
- "three" => "four",
- "five" => false,
- "seven" => "eight",
- "nine" => true,
- "eleven" => "twelve"
- }
-
- mkdef = proc { |hash|
- hash.collect { |arg, value|
- "$%s = %s" % [arg, value]
- }.join(", ")
- }
- manifest = File.join(basedir, "manifest")
- File.open(manifest, "w") { |f|
- f.puts "
- define method(#{mkdef.call(defs1)}, $other) {
- $variable = $testing
- }
-
- define othermethod(#{mkdef.call(defs2)}, $goodness) {
- $more = less
- }
-
- method {
- other => yayness
- }
-
- othermethod {
- goodness => rahness
- }
-"
-
- }
-
- ast = nil
- assert_nothing_raised("Could not parse multiple files") {
- parser = Puppet::Parser::Parser.new()
- parser.file = manifest
- ast = parser.parse
- }
-
- assert(ast, "Did not receive AST while parsing defaults")
-
- scope = nil
- assert_nothing_raised("Could not evaluate defaults parse tree") {
- scope = Puppet::Parser::Scope.new()
- scope.name = "parsetest"
- scope.type = "parsetest"
- objects = scope.evaluate(:ast => ast)
- }
-
- method = nil
- othermethod = nil
- assert_nothing_raised {
- method = scope.find { |child|
- child.is_a?(Puppet::Parser::Scope) and child.type == "method"
- }
- defs1.each { |var, value|
- curval = method.lookupvar(var)
- assert_equal(value, curval, "Did not get default")
- }
- }
-
- assert_nothing_raised {
- method = scope.find { |child|
- child.is_a?(Puppet::Parser::Scope) and child.type == "othermethod"
- }
- defs2.each { |var, value|
- curval = method.lookupvar(var)
- assert_equal(value, curval, "Did not get default")
- }
- }
- end
-
def test_trailingcomma
path = tempfile()
str = %{file { "#{path}": ensure => file, }
}
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.string = str
assert_nothing_raised("Could not parse trailing comma") {
@@ -216,7 +133,7 @@ class TestParser < Test::Unit::TestCase
f.puts %{import "#{imported}"\ninclude foo}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = importer
# Make sure it parses fine
@@ -256,7 +173,7 @@ class TestParser < Test::Unit::TestCase
f.puts %{class local { file { "#{localmaker}": ensure => file }}}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = importer
# Make sure it parses
@@ -288,7 +205,7 @@ class TestParser < Test::Unit::TestCase
f.puts %{class local { file { "#{file}": ensure => file }}}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = importer
assert_nothing_raised {
@@ -325,7 +242,7 @@ class TestParser < Test::Unit::TestCase
f.puts %{import "subdir/subtwo"}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = top
assert_nothing_raised {
@@ -333,46 +250,21 @@ class TestParser < Test::Unit::TestCase
}
end
- # Verify that collectable objects are marked that way.
- def test_collectable
- Puppet[:storeconfigs] = true
- ["@port { ssh: protocols => tcp, number => 22 }",
- "@port { ssh: protocols => tcp, number => 22;
- smtp: protocols => tcp, number => 25 }"].each do |text|
- parser = Puppet::Parser::Parser.new
- parser.string = text
-
- ret = nil
- assert_nothing_raised {
- ret = parser.parse
- }
-
- assert_instance_of(AST::ASTArray, ret)
-
- ret.each do |obj|
- assert_instance_of(AST::ObjectDef, obj)
- assert(obj.collectable, "Object was not marked collectable")
- end
- end
- end
-
# Defaults are purely syntactical, so it doesn't make sense to be able to
# collect them.
def test_uncollectabledefaults
string = "@Port { protocols => tcp }"
- parser = Puppet::Parser::Parser.new
- parser.string = string
assert_raise(Puppet::ParseError) {
- parser.parse
+ mkparser.parse(string)
}
end
# Verify that we can parse collections
def test_collecting
Puppet[:storeconfigs] = true
- text = "port <| |>"
- parser = Puppet::Parser::Parser.new
+ text = "Port <| |>"
+ parser = mkparser
parser.string = text
ret = nil
@@ -392,7 +284,7 @@ class TestParser < Test::Unit::TestCase
File.open(file, "w") do |f|
f.puts %{}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = file
assert_nothing_raised {
parser.parse
@@ -412,7 +304,7 @@ node nodeA, nodeB {
}
end
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.file = file
ast = nil
assert_nothing_raised {
@@ -423,7 +315,7 @@ node nodeA, nodeB {
def test_emptyarrays
str = %{$var = []\n}
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.string = str
# Make sure it parses fine
@@ -442,7 +334,7 @@ file { "/tmp/yayness":
ensure => exists
}
}
- parser = Puppet::Parser::Parser.new
+ parser = mkparser
parser.string = str
# Make sure it parses fine
@@ -452,32 +344,21 @@ file { "/tmp/yayness":
end
def test_metaparams_in_definition_prototypes
- parser = Puppet::Parser::Parser.new
-
- str1 = %{define mydef($schedule) {}}
- parser.string = str1
+ parser = mkparser
- assert_raise(Puppet::ParseError) {
- parser.parse
- }
-
- str2 = %{define mydef($schedule = false) {}}
- parser.string = str2
assert_raise(Puppet::ParseError) {
- parser.parse
+ parser.parse %{define mydef($schedule) {}}
}
- str3 = %{define mydef($schedule = daily) {}}
- parser.string = str3
-
assert_nothing_raised {
- parser.parse
+ parser.parse %{define adef($schedule = false) {}}
+ parser.parse %{define mydef($schedule = daily) {}}
}
end
def test_parsingif
- parser = Puppet::Parser::Parser.new()
+ parser = mkparser
exec = proc do |val|
%{exec { "/bin/echo #{val}": logoutput => true }}
end
@@ -494,6 +375,208 @@ file { "/tmp/yayness":
assert_instance_of(Puppet::Parser::AST::IfStatement, ret)
assert_instance_of(Puppet::Parser::AST::Else, ret.else)
end
+
+ def test_hostclass
+ parser = mkparser
+ interp = parser.interp
+
+ assert_nothing_raised {
+ parser.parse %{class myclass { class other {} }}
+ }
+ assert(interp.findclass("", "myclass"), "Could not find myclass")
+ assert(interp.findclass("", "myclass::other"), "Could not find myclass::other")
+
+ assert_nothing_raised {
+ parser.parse "class base {}
+ class container {
+ class deep::sub inherits base {}
+ }"
+ }
+ sub = interp.findclass("", "container::deep::sub")
+ assert(sub, "Could not find sub")
+ assert_equal("base", sub.parentclass.type)
+ end
+
+ def test_topnamespace
+ parser = mkparser
+ parser.interp.clear
+
+ # Make sure we put the top-level code into a class called "" in
+ # the "" namespace
+ assert_nothing_raised do
+ out = parser.parse ""
+
+ assert_nil(out)
+ assert_nil(parser.interp.findclass("", ""))
+ end
+
+ # Now try something a touch more complicated
+ parser.interp.clear
+ assert_nothing_raised do
+ out = parser.parse "Exec { path => '/usr/bin:/usr/sbin' }"
+ assert_instance_of(AST::ASTArray, out)
+ assert_equal("", parser.interp.findclass("", "").type)
+ assert_equal("", parser.interp.findclass("", "").namespace)
+ assert_equal(out.object_id, parser.interp.findclass("", "").code.object_id)
+ end
+ end
+
+ # Make sure virtual and exported resources work appropriately.
+ def test_virtualresources
+ Puppet[:storeconfigs] = true
+ [:virtual, :exported].each do |form|
+ parser = mkparser
+
+ if form == :virtual
+ at = "@"
+ else
+ at = "@@"
+ end
+
+ check = proc do |res|
+ # Real resources get marked virtual when exported
+ if form == :virtual or res.is_a?(Puppet::Parser::Resource)
+ assert(res.virtual, "Resource #{res.class} is not virtual")
+ end
+ if form == :virtual
+ assert(! res.exported, "Resource #{res.type} is exported")
+ else
+ assert(res.exported, "Resource #{res.type} is not exported")
+ end
+ end
+
+ ret = nil
+ assert_nothing_raised do
+ ret = parser.parse("#{at}file { '/tmp/testing': owner => root }")
+ end
+
+ assert_equal("/tmp/testing", ret[0].title.value)
+ # We always get an astarray back, so...
+ assert_instance_of(AST::ResourceDef, ret[0])
+ check.call(ret[0])
+
+ # Now let's try it with multiple resources in the same spec
+ assert_nothing_raised do
+ ret = parser.parse("#{at}file { ['/tmp/1', '/tmp/2']: owner => root }")
+ end
+
+ assert_instance_of(AST::ASTArray, ret)
+ ret.each do |res|
+ assert_instance_of(AST::ResourceDef, res)
+ check.call(res)
+ end
+
+ # Now evaluate these
+ scope = mkscope
+
+ klass = scope.interp.newclass ""
+ scope.source = klass
+
+ assert_nothing_raised do
+ ret.evaluate :scope => scope
+ end
+
+ # Make sure we can find both of them
+ %w{/tmp/1 /tmp/2}.each do |title|
+ res = scope.findresource("file[#{title}]")
+ assert(res, "Could not find %s" % title)
+ check.call(res)
+ end
+ end
+ end
+
+ def test_collections
+ Puppet[:storeconfigs] = true
+ [:virtual, :exported].each do |form|
+ parser = mkparser
+
+ if form == :virtual
+ arrow = "<||>"
+ else
+ arrow = "<<||>>"
+ end
+
+ check = proc do |coll|
+ assert_instance_of(AST::Collection, coll)
+ assert_equal(form, coll.form)
+ end
+
+ ret = nil
+ assert_nothing_raised do
+ ret = parser.parse("File #{arrow}")
+ end
+ check.call(ret[0])
+ end
+ end
+
+ def test_collectionexpressions
+ %w{== !=}.each do |oper|
+ str = "File <| title #{oper} '/tmp/testing' |>"
+
+ parser = mkparser
+
+ res = nil
+ assert_nothing_raised do
+ res = parser.parse(str)[0]
+ end
+
+ assert_instance_of(AST::Collection, res)
+
+ query = res.query
+ assert_instance_of(AST::CollExpr, query)
+
+ assert_equal(:virtual, query.form)
+ assert_equal("title", query.test1.value)
+ assert_equal("/tmp/testing", query.test2.value)
+ assert_equal(oper, query.oper)
+ end
+ end
+
+ def test_collectionstatements
+ %w{and or}.each do |joiner|
+ str = "File <| title == '/tmp/testing' #{joiner} owner == root |>"
+
+ parser = mkparser
+
+ res = nil
+ assert_nothing_raised do
+ res = parser.parse(str)[0]
+ end
+
+ assert_instance_of(AST::Collection, res)
+
+ query = res.query
+ assert_instance_of(AST::CollExpr, query)
+
+ assert_equal(joiner, query.oper)
+ assert_instance_of(AST::CollExpr, query.test1)
+ assert_instance_of(AST::CollExpr, query.test2)
+ end
+ end
+
+ def test_collectionstatements_with_parens
+ [
+ "(title == '/tmp/testing' and owner == root) or owner == wheel",
+ "(title == '/tmp/testing')"
+ ].each do |test|
+ str = "File <| #{test} |>"
+ parser = mkparser
+
+ res = nil
+ assert_nothing_raised("Could not parse '#{test}'") do
+ res = parser.parse(str)[0]
+ end
+
+ assert_instance_of(AST::Collection, res)
+
+ query = res.query
+ assert_instance_of(AST::CollExpr, query)
+
+ #assert_equal(joiner, query.oper)
+ #assert_instance_of(AST::CollExpr, query.test1)
+ #assert_instance_of(AST::CollExpr, query.test2)
+ end
+ end
end
# $Id$
diff --git a/test/language/rails.rb b/test/language/rails.rb
deleted file mode 100755
index ada4e0915..000000000
--- a/test/language/rails.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/ruby
-
-require 'puppet'
-require 'puppet/rails'
-require 'puppet/parser/interpreter'
-require 'puppet/parser/parser'
-require 'puppet/client'
-require 'puppettest'
-
-class TestRails < Test::Unit::TestCase
- include PuppetTest::ParserTesting
-
- def test_includerails
- assert_nothing_raised {
- require 'puppet/rails'
- }
- end
-
- # Don't do any tests w/out this class
- if defined? ActiveRecord::Base
- def test_hostcache
- # First make some objects
- bucket = mk_transtree do |object, depth, width|
- # and mark some of them collectable
- if width % 2 == 1
- object.collectable = true
- end
- end
-
- # Now collect our facts
- facts = {}
- Facter.each do |fact, value| facts[fact] = value end
-
- assert_nothing_raised {
- Puppet::Rails.init
- }
-
- # Now try storing our crap
- host = nil
- assert_nothing_raised {
- host = Puppet::Rails::Host.store(
- :objects => bucket,
- :facts => facts,
- :host => facts["hostname"]
- )
- }
-
- assert(host, "Did not create host")
-
- host = nil
- assert_nothing_raised {
- host = Puppet::Rails::Host.find_by_name(facts["hostname"])
- }
- assert(host, "Could not find host object")
-
- assert(host.rails_objects, "No objects on host")
-
- assert_equal(facts["hostname"], host.facts["hostname"],
- "Did not retrieve facts")
-
- inline_test_objectcollection(host)
- end
-
- # This is called from another test, it just makes sense to split it out some
- def inline_test_objectcollection(host)
- # XXX For some reason, find_all doesn't work here at all.
- collectable = []
- host.rails_objects.each do |obj|
- if obj.collectable?
- collectable << obj
- end
- end
-
- assert(collectable.length > 0, "Found no collectable objects")
-
- collectable.each do |obj|
- trans = nil
- assert_nothing_raised {
- trans = obj.to_trans
- }
- # Make sure that the objects do not retain their collectable
- # nature.
- assert(!trans.collectable, "Object from db was collectable")
- end
-
- # Now find all collectable objects directly through database APIs
-
- list = Puppet::Rails::RailsObject.find_all_by_collectable(true)
-
- assert_equal(collectable.length, list.length,
- "Did not get the right number of objects")
- end
- else
- $stderr.puts "Install Rails for Rails and Caching tests"
- end
-end
-
-# $Id$
diff --git a/test/language/resource.rb b/test/language/resource.rb
new file mode 100755
index 000000000..12b1e7d2a
--- /dev/null
+++ b/test/language/resource.rb
@@ -0,0 +1,391 @@
+#!/usr/bin/ruby
+
+require 'puppettest'
+require 'puppettest/resourcetesting'
+require 'puppettest/railstesting'
+
+class TestResource < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
+ include PuppetTest::RailsTesting
+ Parser = Puppet::Parser
+ AST = Parser::AST
+
+ def setup
+ super
+ Puppet[:trace] = false
+ @interp, @scope, @source = mkclassframing
+ end
+
+ def test_initialize
+ args = {:type => "resource", :title => "testing",
+ :source => @source, :scope => @scope}
+ # Check our arg requirements
+ args.each do |name, value|
+ try = args.dup.delete(name)
+ assert_raise(Puppet::DevError) do
+ Parser::Resource.new(try)
+ end
+ end
+
+ args[:params] = paramify @source, :one => "yay", :three => "rah"
+
+ res = nil
+ assert_nothing_raised do
+ res = Parser::Resource.new(args)
+ end
+
+ # Make sure it got the parameters correctly.
+ assert_equal("yay", res[:one])
+ assert_equal("rah", res[:three])
+
+ assert_equal({:one => "yay", :three => "rah"}, res.to_hash)
+ end
+
+ def test_override
+ res = mkresource
+
+ # Now verify we can't override with any random class
+ assert_raise(Puppet::ParseError) do
+ res.set paramify(@scope.findclass("other"), "one" => "boo").shift
+ end
+
+ # And that we can with a subclass
+ assert_nothing_raised do
+ res.set paramify(@scope.findclass("sub1"), "one" => "boo").shift
+ end
+
+ # And that a different subclass can override a different parameter
+ assert_nothing_raised do
+ res.set paramify(@scope.findclass("sub2"), "three" => "boo").shift
+ end
+
+ # But not the same one
+ assert_raise(Puppet::ParseError) do
+ res.set paramify(@scope.findclass("sub2"), "one" => "something").shift
+ end
+ end
+
+ def test_merge
+ # Start with the normal one
+ res = mkresource
+
+ # Now create a resource from a different scope
+ other = mkresource :source => other, :params => {"one" => "boo"}
+
+ # Make sure we can't merge it
+ assert_raise(Puppet::ParseError) do
+ res.merge(other)
+ end
+
+ # Make one from a subscope
+ other = mkresource :source => "sub1", :params => {"one" => "boo"}
+
+ # Make sure it merges
+ assert_nothing_raised do
+ res.merge(other)
+ end
+
+ assert_equal("boo", res["one"])
+ end
+
+ def test_paramcheck
+ # First make a builtin resource
+ res = nil
+ assert_nothing_raised do
+ res = Parser::Resource.new :type => "file", :title => tempfile(),
+ :source => @source, :scope => @scope
+ end
+
+ %w{path group source schedule subscribe}.each do |param|
+ assert_nothing_raised("Param %s was considered invalid" % param) do
+ res.paramcheck(param)
+ end
+ end
+
+ %w{this bad noness}.each do |param|
+ assert_raise(Puppet::ParseError, "%s was considered valid" % param) do
+ res.paramcheck(param)
+ end
+ end
+
+ # Now create a defined resource
+ assert_nothing_raised do
+ res = Parser::Resource.new :type => "resource", :title => "yay",
+ :source => @source, :scope => @scope
+ end
+
+ %w{one two three schedule subscribe}.each do |param|
+ assert_nothing_raised("Param %s was considered invalid" % param) do
+ res.paramcheck(param)
+ end
+ end
+
+ %w{this bad noness}.each do |param|
+ assert_raise(Puppet::ParseError, "%s was considered valid" % param) do
+ res.paramcheck(param)
+ end
+ end
+ end
+
+ def test_to_trans
+ # First try translating a builtin resource
+ res = Parser::Resource.new :type => "file", :title => "/tmp",
+ :source => @source, :scope => @scope,
+ :params => paramify(@source, :owner => "nobody", :mode => "644")
+
+ obj = nil
+ assert_nothing_raised do
+ obj = res.to_trans
+ end
+
+ assert_instance_of(Puppet::TransObject, obj)
+
+ assert_equal(obj.type, res.type)
+ assert_equal(obj.name, res.title)
+
+ # TransObjects use strings, resources use symbols
+ hash = obj.to_hash.inject({}) { |h,a| h[a[0].intern] = a[1]; h }
+ assert_equal(hash, res.to_hash)
+ end
+
+ def test_adddefaults
+ # Set some defaults at the top level
+ top = {:one => "fun", :two => "shoe"}
+
+ @scope.setdefaults("resource", paramify(@source, top))
+
+ # Make a resource at that level
+ res = Parser::Resource.new :type => "resource", :title => "yay",
+ :source => @source, :scope => @scope
+
+ # Add the defaults
+ assert_nothing_raised do
+ res.adddefaults
+ end
+
+ # And make sure we got them
+ top.each do |p, v|
+ assert_equal(v, res[p])
+ end
+
+ # Now got a bit lower
+ other = @scope.newscope
+
+ # And create a resource
+ lowerres = Parser::Resource.new :type => "resource", :title => "funtest",
+ :source => @source, :scope => other
+
+ assert_nothing_raised do
+ lowerres.adddefaults
+ end
+
+ # And check
+ top.each do |p, v|
+ assert_equal(v, lowerres[p])
+ end
+
+ # Now add some of our own defaults
+ lower = {:one => "shun", :three => "free"}
+ other.setdefaults("resource", paramify(@source, lower))
+ otherres = Parser::Resource.new :type => "resource", :title => "yaytest",
+ :source => @source, :scope => other
+
+ should = top.dup
+ # Make sure the lower defaults beat the higher ones.
+ lower.each do |p, v| should[p] = v end
+
+ otherres.adddefaults
+
+ should.each do |p,v|
+ assert_equal(v, otherres[p])
+ end
+ end
+
+ def test_evaluate
+ # Make a definition that we know will, um, do something
+ @interp.newdefine "evaltest",
+ :arguments => [%w{one}, ["two", stringobj("755")]],
+ :code => resourcedef("file", "/tmp",
+ "owner" => varref("one"), "mode" => varref("two"))
+
+ res = Parser::Resource.new :type => "evaltest", :title => "yay",
+ :source => @source, :scope => @scope,
+ :params => paramify(@source, :one => "nobody")
+
+ # Now try evaluating
+ ret = nil
+ assert_nothing_raised do
+ ret = res.evaluate
+ end
+
+ # Make sure we can find our object now
+ result = @scope.findresource("file[/tmp]")
+
+ # Now make sure we got the code we expected.
+ assert_instance_of(Puppet::Parser::Resource, result)
+
+ assert_equal("file", result.type)
+ assert_equal("/tmp", result.title)
+ assert_equal("nobody", result["owner"])
+ assert_equal("755", result["mode"])
+
+ # And that we cannot find the old resource
+ assert_nil(@scope.findresource("evaltest[yay]"),
+ "Evaluated resource was not deleted")
+ end
+
+ def test_addoverrides
+ # First create an override for an object that doesn't yet exist
+ over1 = mkresource :source => "sub1", :params => {:one => "yay"}
+
+ assert_nothing_raised do
+ @scope.setoverride(over1)
+ end
+
+ assert(over1.override, "Override was not marked so")
+
+ # Now make the resource
+ res = mkresource :source => "base", :params => {:one => "rah",
+ :three => "foo"}
+
+ # And add it to our scope
+ @scope.setresource(res)
+
+ # And make sure over1 has not yet taken affect
+ assert_equal("foo", res[:three], "Lost value")
+
+ # Now add an immediately binding override
+ over2 = mkresource :source => "sub1", :params => {:three => "yay"}
+
+ assert_nothing_raised do
+ @scope.setoverride(over2)
+ end
+
+ # And make sure it worked
+ assert_equal("yay", res[:three], "Override 2 was ignored")
+
+ # Now add our late-binding override
+ assert_nothing_raised do
+ res.addoverrides
+ end
+
+ # And make sure they're still around
+ assert_equal("yay", res[:one], "Override 1 lost")
+ assert_equal("yay", res[:three], "Override 2 lost")
+
+ # And finally, make sure that there are no remaining overrides
+ assert_nothing_raised do
+ res.addoverrides
+ end
+ end
+
+ def test_proxymethods
+ res = Parser::Resource.new :type => "evaltest", :title => "yay",
+ :source => @source, :scope => @scope
+
+ assert_equal("evaltest", res.type)
+ assert_equal("yay", res.title)
+ assert_equal(false, res.builtin?)
+ end
+
+ def test_addmetaparams
+ res = Parser::Resource.new :type => "evaltest", :title => "yay",
+ :source => @source, :scope => @scope
+
+ assert_nil(res[:schedule], "Got schedule already")
+ @scope.setvar("schedule", "daily")
+
+ assert_nothing_raised do
+ res.addmetaparams
+ end
+
+ assert_equal("daily", res[:schedule], "Did not get metaparam")
+ assert_nil(res[:noop], "Got invalid metaparam")
+ end
+
+ def test_reference_conversion
+ # First try it as a normal string
+ ref = Parser::Resource::Reference.new(:type => "file", :title => "/tmp/ref1")
+
+ # Now create an obj that uses it
+ res = mkresource :type => "file", :title => "/tmp/resource",
+ :params => {:require => ref}
+
+ trans = nil
+ assert_nothing_raised do
+ trans = res.to_trans
+ end
+
+ assert_instance_of(Array, trans["require"])
+ assert_equal(["file", "/tmp/ref1"], trans["require"])
+
+ # Now try it when using an array of references.
+ two = Parser::Resource::Reference.new(:type => "file", :title => "/tmp/ref2")
+ res = mkresource :type => "file", :title => "/tmp/resource2",
+ :params => {:require => [ref, two]}
+
+ trans = nil
+ assert_nothing_raised do
+ trans = res.to_trans
+ end
+
+ assert_instance_of(Array, trans["require"][0])
+ trans["require"].each do |val|
+ assert_instance_of(Array, val)
+ assert_equal("file", val[0])
+ assert(val[1] =~ /\/tmp\/ref[0-9]/,
+ "Was %s instead of the file name" % val[1])
+ end
+ end
+
+ # This is a bit of a weird one -- the user should not actually know
+ # that components exist, so we want references to act like they're not
+ # builtin
+ def test_components_are_not_builtin
+ ref = Parser::Resource::Reference.new(:type => "component", :title => "yay")
+
+ assert_nil(ref.builtintype, "Component was considered builtin")
+ end
+ if defined? ActiveRecord::Base
+ def test_store
+ railsinit
+ res = mkresource :type => "file", :title => "/tmp/testing",
+ :source => @source, :scope => @scope,
+ :params => {:owner => "root", :mode => "755"}
+
+ # We also need a Rails Host to store under
+ host = Puppet::Rails::Host.new(:name => Facter.hostname)
+
+ obj = nil
+ assert_nothing_raised do
+ obj = res.store(host)
+ end
+
+ assert_instance_of(Puppet::Rails::RailsResource, obj)
+
+ assert_nothing_raised do
+ Puppet::Util.benchmark(:info, "Saved host") do
+ host.save
+ end
+ end
+
+ # Now make sure we can find it again
+ assert_nothing_raised do
+ obj = Puppet::Rails::RailsResource.find_by_host_id_and_restype_and_title(
+ host.id, res.type, res.title
+ )
+ end
+ assert_instance_of(Puppet::Rails::RailsResource, obj)
+
+ # Make sure we get the parameters back
+ obj.rails_parameters.each do |param|
+ assert_equal(res[param[:name]], param[:value],
+ "%s was different" % param[:name])
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/test/language/scope.rb b/test/language/scope.rb
index 85189627d..b9401e38b 100755
--- a/test/language/scope.rb
+++ b/test/language/scope.rb
@@ -5,6 +5,8 @@ require 'puppet/parser/interpreter'
require 'puppet/parser/parser'
require 'puppet/client'
require 'puppettest'
+require 'puppettest/parsertesting'
+require 'puppettest/resourcetesting'
# so, what kind of things do we want to test?
@@ -17,6 +19,7 @@ require 'puppettest'
class TestScope < Test::Unit::TestCase
include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
def to_ary(hash)
hash.collect { |key,value|
@@ -35,7 +38,7 @@ class TestScope < Test::Unit::TestCase
10.times { |index|
# slap some recursion in there
- scope = Puppet::Parser::Scope.new(:parent => scope)
+ scope = mkscope(:parent => scope)
scopes.push scope
var = "var%s" % index
@@ -97,8 +100,8 @@ class TestScope < Test::Unit::TestCase
def test_declarative
# set to declarative
- top = Puppet::Parser::Scope.new(:declarative => true)
- sub = Puppet::Parser::Scope.new(:parent => top)
+ top = mkscope(:declarative => true)
+ sub = mkscope(:parent => top)
assert_nothing_raised {
top.setvar("test","value")
@@ -116,8 +119,8 @@ class TestScope < Test::Unit::TestCase
def test_notdeclarative
# set to not declarative
- top = Puppet::Parser::Scope.new(:declarative => false)
- sub = Puppet::Parser::Scope.new(:parent => top)
+ top = mkscope(:declarative => false)
+ sub = mkscope(:parent => top)
assert_nothing_raised {
top.setvar("test","value")
@@ -133,106 +136,65 @@ class TestScope < Test::Unit::TestCase
}
end
- def test_defaults
- scope = nil
- over = "over"
-
- scopes = []
- vars = []
- values = {}
- ovalues = []
+ def test_setdefaults
+ interp, scope, source = mkclassframing
- defs = Hash.new { |hash,key|
- hash[key] = Hash.new(nil)
- }
-
- prevdefs = Hash.new { |hash,key|
- hash[key] = Hash.new(nil)
- }
-
- params = %w{a list of parameters that could be used for defaults}
+ # The setdefaults method doesn't really check what we're doing,
+ # so we're just going to use fake defaults here.
- types = %w{a set of types that could be used to set defaults}
+ # First do a simple local lookup
+ params = paramify(source, :one => "fun", :two => "shoe")
+ origshould = {}
+ params.each do |p| origshould[p.name] = p end
+ assert_nothing_raised do
+ scope.setdefaults(:file, params)
+ end
- 10.times { |index|
- scope = Puppet::Parser::Scope.new(:parent => scope)
- scopes.push scope
+ ret = nil
+ assert_nothing_raised do
+ ret = scope.lookupdefaults(:file)
+ end
- tmptypes = []
-
- # randomly create defaults for a random set of types
- tnum = rand(5)
- tnum.times { |t|
- # pick a type
- #Puppet.debug "Type length is %s" % types.length
- #s = rand(types.length)
- #Puppet.debug "Type num is %s" % s
- #type = types[s]
- #Puppet.debug "Type is %s" % s
- type = types[rand(types.length)]
- if tmptypes.include?(type)
- Puppet.debug "Duplicate type %s" % type
- redo
- else
- tmptypes.push type
- end
+ assert_equal(origshould, ret)
- Puppet.debug "type is %s" % type
+ # Now create a subscope and add some more params.
+ newscope = scope.newscope
- d = {}
+ newparams = paramify(source, :one => "shun", :three => "free")
+ assert_nothing_raised {
+ newscope.setdefaults(:file, newparams)
+ }
- # randomly assign some parameters
- num = rand(4)
- num.times { |n|
- param = params[rand(params.length)]
- if d.include?(param)
- Puppet.debug "Duplicate param %s" % param
- redo
- else
- d[param] = rand(1000)
- end
- }
+ # And make sure we get the appropriate ones back
+ should = {}
+ params.each do |p| should[p.name] = p end
+ newparams.each do |p| should[p.name] = p end
- # and then add a consistent type
- d["always"] = rand(1000)
+ assert_nothing_raised do
+ ret = newscope.lookupdefaults(:file)
+ end
- d.each { |var,val|
- defs[type][var] = val
- }
+ assert_equal(should, ret)
- assert_nothing_raised {
- scope.setdefaults(type,to_ary(d))
- }
- fdefs = nil
- assert_nothing_raised {
- fdefs = scope.lookupdefaults(type)
- }
+ # Make sure we still only get the originals from the top scope
+ assert_nothing_raised do
+ ret = scope.lookupdefaults(:file)
+ end
- # now, make sure that reassignment fails if we're
- # in declarative mode
- assert_raise(Puppet::ParseError) {
- scope.setdefaults(type,[%w{always funtest}])
- }
+ assert_equal(origshould, ret)
- # assert that we have collected the same values
- assert_equal(defs[type],fdefs)
+ # Now create another scope and make sure we only get the top defaults
+ otherscope = scope.newscope
+ assert_equal(origshould, otherscope.lookupdefaults(:file))
- # now assert that our parent still finds the same defaults
- # it got last time
- if parent = scope.parent
- unless prevdefs[type].nil?
- assert_equal(prevdefs[type],parent.lookupdefaults(type))
- end
- end
- d.each { |var,val|
- prevdefs[type][var] = val
- }
- }
- }
+ # And make sure none of the scopes has defaults for other types
+ [scope, newscope, otherscope].each do |sc|
+ assert_equal({}, sc.lookupdefaults(:exec))
+ end
end
def test_strinterp
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope()
assert_nothing_raised {
scope.setvar("test","value")
@@ -264,313 +226,61 @@ class TestScope < Test::Unit::TestCase
assert_equal("$test string", val)
end
- # Test some of the host manipulations
- def test_hostlookup
- top = Puppet::Parser::Scope.new()
-
- # Create a deep scope tree, so that we know we're doing a deeply recursive
- # search.
- mid1 = Puppet::Parser::Scope.new(:parent => top)
- mid2 = Puppet::Parser::Scope.new(:parent => mid1)
- mid3 = Puppet::Parser::Scope.new(:parent => mid2)
- child1 = Puppet::Parser::Scope.new(:parent => mid3)
- mida = Puppet::Parser::Scope.new(:parent => top)
- midb = Puppet::Parser::Scope.new(:parent => mida)
- midc = Puppet::Parser::Scope.new(:parent => midb)
- child2 = Puppet::Parser::Scope.new(:parent => midc)
-
- # verify we can set a host
- assert_nothing_raised("Could not create host") {
- child1.setnode("testing", AST::Node.new(
- :type => "testing",
- :code => :notused
- )
- )
- }
-
- # Verify we cannot redefine it
- assert_raise(Puppet::ParseError, "Duplicate host creation succeeded") {
- child2.setnode("testing", AST::Node.new(
- :type => "testing",
- :code => :notused
- )
- )
- }
-
- # Now verify we can find the host again
- host = nil
- assert_nothing_raised("Host lookup failed") {
- hash = top.node("testing")
- host = hash[:node]
- }
-
- assert(host, "Could not find host")
- assert(host.code == :notused, "Host is not what we stored")
- end
-
- # Verify that two statements about a file within the same scope tree
- # will cause a conflict.
- def test_noconflicts
- filename = tempfile()
- children = []
-
- # create the parent class
- children << classobj("one", :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "root")
- ]
- ))
-
- # now create a child class with differ values
- children << classobj("two",
- :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "bin")
- ]
- ))
-
- # Now call the child class
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("two"),
- :name => nameobj("yayness"),
- :params => astarray()
- ) << AST::ObjectDef.new(
- :type => nameobj("one"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
- }
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- objects = nil
- scope = nil
-
- # Here's where we should encounter the failure. It should find that
- # it has already created an object with that name, and this should result
- # in some pukey-pukeyness.
- assert_raise(Puppet::ParseError) {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
- end
-
- # Verify that statements about the same element within the same scope
- # cause a conflict.
- def test_failonconflictinsamescope
- filename = tempfile()
- children = []
-
- # Now call the child class
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << fileobj(filename, "owner" => "root")
- children << fileobj(filename, "owner" => "bin")
- }
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- objects = nil
- scope = nil
-
- # Here's where we should encounter the failure. It should find that
- # it has already created an object with that name, and this should result
- # in some pukey-pukeyness.
- assert_raise(Puppet::ParseError) {
- scope = Puppet::Parser::Scope.new()
- scope.top = true
- objects = scope.evaluate(:ast => top)
- }
- end
-
- # Verify that we override statements that we find within our scope
- def test_suboverrides
- filename = tempfile()
- children = []
-
- # create the parent class
- children << classobj("parent", :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "root")
- ]
- ))
-
- # now create a child class with differ values
- children << classobj("child", :parentclass => nameobj("parent"),
- :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "bin")
- ]
- ))
-
- # Now call the child class
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("child"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
- }
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- objects = nil
- scope = nil
- assert_nothing_raised("Could not evaluate") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
-
- assert_equal(1, objects.length, "Returned too many objects: %s" %
- objects.inspect)
-
- assert_equal(1, objects[0].length, "Returned too many objects: %s" %
- objects[0].inspect)
-
- assert_nothing_raised {
- file = objects[0][0]
- assert_equal("bin", file["owner"], "Value did not override correctly")
- }
- end
-
- def test_multipletypes
- scope = Puppet::Parser::Scope.new()
- children = []
-
- # create the parent class
- children << classobj("aclass")
- children << classobj("aclass")
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
-
- scope = nil
- assert_raise(Puppet::ParseError) {
- scope = Puppet::Parser::Scope.new()
- objects = top.evaluate(:scope => scope)
- }
- end
-
- # Verify that definitions have a different context than classes.
- def test_newsubcontext
- filename = tempfile()
- children = []
-
- # Create a component
- children << compobj("comp", :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "root" )
- ]
- ))
-
- # Now create a class that modifies the same file and also
- # calls the component
- children << classobj("klass", :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "bin" ),
- AST::ObjectDef.new(
- :type => nameobj("comp"),
- :params => astarray()
- )
- ]
- ))
-
- # Now call the class
- children << AST::ObjectDef.new(
- :type => nameobj("klass"),
- :params => astarray()
- )
-
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
+ def test_setclass
+ interp, scope, source = mkclassframing
- trans = nil
- scope = nil
- #assert_nothing_raised {
- assert_raise(Puppet::ParseError, "A conflict was allowed") {
- scope = Puppet::Parser::Scope.new()
- trans = scope.evaluate(:ast => top)
- }
- # scope = Puppet::Parser::Scope.new()
- # trans = scope.evaluate(:ast => top)
- #}
- end
+ base = scope.findclass("base")
+ assert(base, "Could not find base class")
+ assert(! scope.setclass?(base), "Class incorrectly set")
+ assert(! scope.classlist.include?("base"), "Class incorrectly in classlist")
+ assert_nothing_raised do
+ scope.setclass base
+ end
- def test_defaultswithmultiplestatements
- path = tempfile()
+ assert(scope.setclass?(base), "Class incorrectly unset")
+ assert(scope.classlist.include?("base"), "Class not in classlist")
- stats = []
- stats << defaultobj("file", "group" => "root")
- stats << fileobj(path, "owner" => "root")
- stats << fileobj(path, "mode" => "755")
+ # Now try it with a normal string
+ assert_raise(Puppet::DevError) do
+ scope.setclass "string"
+ end
- top = AST::ASTArray.new(
- :file => __FILE__,
- :line => __LINE__,
- :children => stats
- )
- scope = Puppet::Parser::Scope.new()
- trans = nil
- assert_nothing_raised {
- trans = scope.evaluate(:ast => top)
- }
+ assert(! scope.setclass?("string"), "string incorrectly set")
- obj = trans.find do |obj| obj.is_a? Puppet::TransObject end
+ # Set "" in the class list, and make sure it doesn't show up in the return
+ top = scope.findclass("")
+ assert(top, "Could not find top class")
+ scope.setclass top
- assert(obj, "Could not retrieve file obj")
- assert_equal("root", obj["group"], "Default did not take")
- assert_equal("root", obj["owner"], "Owner did not take")
- assert_equal("755", obj["mode"], "Mode did not take")
+ assert(! scope.classlist.include?(""), "Class list included empty")
end
- def test_validclassnames
- scope = Puppet::Parser::Scope.new()
+ def test_validtags
+ scope = mkscope()
- ["a class", "Class", "a.class"].each do |bad|
+ ["a class", "a.class"].each do |bad|
assert_raise(Puppet::ParseError, "Incorrectly allowed %s" % bad.inspect) do
- scope.setclass(object_id, bad)
+ scope.tag(bad)
end
end
- ["a-class", "a_class", "class", "yayNess"].each do |good|
+ ["a-class", "a_class", "Class", "class", "yayNess"].each do |good|
assert_nothing_raised("Incorrectly banned %s" % good.inspect) do
- scope.setclass(object_id, good)
+ scope.tag(good)
end
end
end
def test_tagfunction
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope()
assert_nothing_raised {
scope.function_tag(["yayness", "booness"])
}
- assert(scope.classlist.include?("yayness"), "tag 'yayness' did not get set")
- assert(scope.classlist.include?("booness"), "tag 'booness' did not get set")
+ assert(scope.tags.include?("yayness"), "tag 'yayness' did not get set")
+ assert(scope.tags.include?("booness"), "tag 'booness' did not get set")
# Now verify that the 'tagged' function works correctly
assert(scope.function_tagged("yayness"),
@@ -583,77 +293,37 @@ class TestScope < Test::Unit::TestCase
end
def test_includefunction
- scope = Puppet::Parser::Scope.new()
-
- one = tempfile()
- two = tempfile()
-
- children = []
-
- children << classobj("one", :code => AST::ASTArray.new(
- :children => [
- fileobj(one, "owner" => "root")
- ]
- ))
+ interp = mkinterp
+ scope = mkscope :interp => interp
- children << classobj("two", :code => AST::ASTArray.new(
- :children => [
- fileobj(two, "owner" => "root")
- ]
- ))
+ myclass = interp.newclass "myclass"
+ otherclass = interp.newclass "otherclass"
- children << Puppet::Parser::AST::Function.new(
+ function = Puppet::Parser::AST::Function.new(
:name => "include",
:ftype => :statement,
:arguments => AST::ASTArray.new(
- :children => [nameobj("one"), nameobj("two")]
+ :children => [nameobj("myclass"), nameobj("otherclass")]
)
)
- top = AST::ASTArray.new(:children => children)
-
- #assert_nothing_raised {
- # scope.function_include(["one", "two"])
- #}
-
- assert_nothing_raised {
- scope.evaluate(:ast => top)
- }
-
-
- assert(scope.classlist.include?("one"), "tag 'one' did not get set")
- assert(scope.classlist.include?("two"), "tag 'two' did not get set")
+ assert_nothing_raised do
+ function.evaluate :scope => scope
+ end
- # Now verify that the 'tagged' function works correctly
- assert(scope.function_tagged("one"),
- "tagged function incorrectly returned false")
- assert(scope.function_tagged("two"),
- "tagged function incorrectly returned false")
+ [myclass, otherclass].each do |klass|
+ assert(scope.setclass?(klass),
+ "%s was not set" % klass.fqname)
+ end
end
def test_definedfunction
- scope = Puppet::Parser::Scope.new()
-
- one = tempfile()
- two = tempfile()
-
- children = []
-
- children << classobj("one", :code => AST::ASTArray.new(
- :children => [
- fileobj(one, "owner" => "root")
- ]
- ))
-
- children << classobj("two", :code => AST::ASTArray.new(
- :children => [
- fileobj(two, "owner" => "root")
- ]
- ))
-
- top = AST::ASTArray.new(:children => children)
+ interp = mkinterp
+ %w{one two}.each do |name|
+ interp.newdefine name
+ end
- top.evaluate(:scope => scope)
+ scope = mkscope :interp => interp
assert_nothing_raised {
%w{one two file user}.each do |type|
@@ -664,40 +334,6 @@ class TestScope < Test::Unit::TestCase
assert(!scope.function_defined(["nopeness"]),
"Class 'nopeness' was incorrectly considered defined")
}
-
-
- end
-
- # Make sure components acquire defaults.
- def test_defaultswithcomponents
- children = []
-
- # Create a component
- filename = tempfile()
- args = AST::ASTArray.new(
- :file => tempfile(),
- :line => rand(100),
- :children => [nameobj("argument")]
- )
- children << compobj("comp", :args => args, :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => varref("argument") )
- ]
- ))
-
- # Create a default
- children << defaultobj("comp", "argument" => "yayness")
-
- # lastly, create an object that calls our third component
- children << objectdef("comp", "boo", {"argument" => "parentfoo"})
-
- trans = assert_evaluate(children)
-
- flat = trans.flatten
-
- assert(!flat.empty?, "Got no objects back")
-
- assert_equal("parentfoo", flat[0]["owner"], "default did not take")
end
# Make sure we know what we consider to be truth.
@@ -714,7 +350,7 @@ class TestScope < Test::Unit::TestCase
# Verify scope context is handled correctly.
def test_scopeinside
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope()
one = :one
two = :two
@@ -763,9 +399,10 @@ class TestScope < Test::Unit::TestCase
end
if defined? ActiveRecord
- # Verify that we recursively mark as collectable the results of collectable
+ # Verify that we recursively mark as exported the results of collectable
# components.
- def test_collectablecomponents
+ def test_exportedcomponents
+ interp, scope, source = mkclassframing
children = []
args = AST::ASTArray.new(
@@ -773,47 +410,44 @@ class TestScope < Test::Unit::TestCase
:line => rand(100),
:children => [nameobj("arg")]
)
+
# Create a top-level component
- children << compobj("one", :args => args)
+ interp.newdefine "one", :arguments => [%w{arg}],
+ :code => AST::ASTArray.new(
+ :children => [
+ resourcedef("file", "/tmp", {"owner" => varref("arg")})
+ ]
+ )
# And a component that calls it
- children << compobj("two", :args => args, :code => AST::ASTArray.new(
- :children => [
- objectdef("one", "ptest", {"arg" => "parentfoo"})
- ]
- ))
+ interp.newdefine "two", :arguments => [%w{arg}],
+ :code => AST::ASTArray.new(
+ :children => [
+ resourcedef("one", "ptest", {"arg" => varref("arg")})
+ ]
+ )
# And then a third component that calls the second
- children << compobj("three", :args => args, :code => AST::ASTArray.new(
- :children => [
- objectdef("two", "yay", {"arg" => "parentfoo"})
- ]
- ))
+ interp.newdefine "three", :arguments => [%w{arg}],
+ :code => AST::ASTArray.new(
+ :children => [
+ resourcedef("two", "yay", {"arg" => varref("arg")})
+ ]
+ )
# lastly, create an object that calls our third component
- obj = objectdef("three", "boo", {"arg" => "parentfoo"})
-
- # And mark it as collectable
- obj.collectable = true
+ obj = resourcedef("three", "boo", {"arg" => "parentfoo"})
- children << obj
+ # And mark it as exported
+ obj.exported = true
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
+ obj.evaluate :scope => scope
- trans = nil
- scope = nil
- assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
- trans = scope.evaluate(:ast => top)
- }
+ # And then evaluate it
+ interp.evaliterate(scope)
%w{file}.each do |type|
- objects = scope.exported(type)
+ objects = scope.lookupexported(type)
assert(!objects.empty?, "Did not get an exported %s" % type)
end
@@ -832,11 +466,11 @@ class TestScope < Test::Unit::TestCase
File.open(file, "w") { |f|
f.puts "
class yay {
- @host { myhost: ip => \"192.168.0.2\" }
+ @@host { myhost: ip => \"192.168.0.2\" }
}
include yay
-@host { puppet: ip => \"192.168.0.3\" }
-host <||>"
+@@host { puppet: ip => \"192.168.0.3\" }
+Host <<||>>"
}
interp = nil
@@ -853,7 +487,7 @@ host <||>"
# if we pull it up from the database.
2.times { |i|
assert_nothing_raised {
- objects = interp.run("localhost", {})
+ objects = interp.run("localhost", {"hostname" => "localhost"})
}
flat = objects.flatten
@@ -863,53 +497,95 @@ host <||>"
end
}
end
+ else
+ $stderr.puts "No ActiveRecord -- skipping collection tests"
+ end
- # Verify that we cannot override differently exported objects
- def test_exportedoverrides
- filename = tempfile()
- children = []
+ # Make sure tags behave appropriately.
+ def test_tags
+ interp, scope, source = mkclassframing
- obj = fileobj(filename, "owner" => "root")
- obj.collectable = true
- # create the parent class
- children << classobj("parent", :code => AST::ASTArray.new(
- :children => [
- obj
- ]
- ))
-
- # now create a child class with differ values
- children << classobj("child", :parentclass => nameobj("parent"),
- :code => AST::ASTArray.new(
- :children => [
- fileobj(filename, "owner" => "bin")
- ]
- ))
-
- # Now call the child class
- assert_nothing_raised("Could not add AST nodes for calling") {
- children << AST::ObjectDef.new(
- :type => nameobj("child"),
- :name => nameobj("yayness"),
- :params => astarray()
- )
- }
+ # First make sure we can only set legal tags
+ ["an invalid tag", "-anotherinvalid", "bad*tag"].each do |tag|
+ assert_raise(Puppet::ParseError, "Tag #{tag} was considered valid") do
+ scope.tag tag
+ end
+ end
- top = nil
- assert_nothing_raised("Could not create top object") {
- top = AST::ASTArray.new(
- :children => children
- )
- }
+ # Now make sure good tags make it through.
+ tags = %w{good-tag yaytag GoodTag another_tag}
+ tags.each do |tag|
+ assert_nothing_raised("Tag #{tag} was considered invalid") do
+ scope.tag tag
+ end
+ end
- objects = nil
- scope = nil
- assert_raise(Puppet::ParseError, "Incorrectly allowed override") {
- scope = Puppet::Parser::Scope.new()
- objects = scope.evaluate(:ast => top)
- }
+ # And make sure we get each of them.
+ ptags = scope.tags
+ tags.each do |tag|
+ assert(ptags.include?(tag), "missing #{tag}")
+ end
+
+
+ # Now create a subscope and set some tags there
+ newscope = scope.newscope(:type => 'subscope')
+
+ # set some tags
+ newscope.tag "onemore", "yaytag"
+
+ # And make sure we get them plus our parent tags
+ assert_equal((ptags + %w{onemore subscope}).sort, newscope.tags.sort)
end
- else
- $stderr.puts "No ActiveRecord -- skipping collection tests"
+
+ # Make sure we successfully translate objects
+ def test_translate
+ interp, scope, source = mkclassframing
+
+ # Create a define that we'll be using
+ interp.newdefine("wrapper", :code => AST::ASTArray.new(:children => [
+ resourcedef("file", varref("name"), "owner" => "root")
+ ]))
+
+ # Now create a resource that uses that define
+ define = mkresource(:type => "wrapper", :title => "/tmp/testing",
+ :scope => scope, :source => source, :params => :none)
+
+ scope.setresource define
+
+ # And a normal resource
+ scope.setresource mkresource(:type => "file", :title => "/tmp/rahness",
+ :scope => scope, :source => source,
+ :params => {:owner => "root"})
+
+ # Evaluate the the define thing.
+ define.evaluate
+
+ # Now the scope should have a resource and a subscope. Translate the
+ # whole thing.
+ ret = nil
+ assert_nothing_raised do
+ ret = scope.translate
+ end
+
+ assert_instance_of(Puppet::TransBucket, ret)
+
+ ret.each do |obj|
+ assert(obj.is_a?(Puppet::TransBucket) || obj.is_a?(Puppet::TransObject),
+ "Got a non-transportable object %s" % obj.class)
+ end
+
+ rahness = ret.find { |c| c.type == "file" and c.name == "/tmp/rahness" }
+ assert(rahness, "Could not find top-level file")
+ assert_equal("root", rahness["owner"])
+
+ bucket = ret.find { |c| c.class == Puppet::TransBucket and c.name == "/tmp/testing" }
+ assert(bucket, "Could not find define bucket")
+
+ testing = bucket.find { |c| c.type == "file" and c.name == "/tmp/testing" }
+ assert(testing, "Could not find define file")
+ assert_equal("root", testing["owner"])
+
end
end
+
+# $Id$
diff --git a/test/language/snippets.rb b/test/language/snippets.rb
index 05ac066eb..f2597c8d0 100755
--- a/test/language/snippets.rb
+++ b/test/language/snippets.rb
@@ -12,7 +12,7 @@ class TestSnippets < Test::Unit::TestCase
include ObjectSpace
def self.snippetdir
- PuppetTest.exampledir "code", "snippets"
+ PuppetTest.datadir "snippets"
end
def snippet(name)
@@ -464,6 +464,13 @@ class TestSnippets < Test::Unit::TestCase
@@tmpfiles << "/tmp/emptyexextest"
end
+ def snippet_multisubs(trans)
+ path = "/tmp/multisubtest"
+ assert(FileTest.exists?(path), "Did not create file")
+ assert_equal("sub2", File.read(path), "sub2 did not override content")
+ assert_equal(0755, filemode(path), "sub1 did not override mode")
+ end
+
def disabled_snippet_dirchmod(trans)
dirs = %w{a b}.collect { |letter|
"/tmp/dirchmodtest%s" % letter
diff --git a/test/language/transportable.rb b/test/language/transportable.rb
index 217bb3370..a25500ee8 100755
--- a/test/language/transportable.rb
+++ b/test/language/transportable.rb
@@ -3,6 +3,7 @@
require 'puppet'
require 'puppet/transportable'
require 'puppettest'
+require 'puppettest/parsertesting'
require 'yaml'
class TestTransportable < Test::Unit::TestCase
diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb
index e7ee99c35..c6ce54b93 100644
--- a/test/lib/puppettest.rb
+++ b/test/lib/puppettest.rb
@@ -4,14 +4,14 @@ require 'test/unit'
module PuppetTest
# Find the root of the Puppet tree; this is not the test directory, but
# the parent of that dir.
- def basedir
+ def basedir(*list)
unless defined? @@basedir
case $0
when /rake_test_loader/
@@basedir = File.dirname(Dir.getwd)
else
dir = nil
- if /^#{File::SEPARATOR}.+\.rb/
+ if $0 =~ /^#{File::SEPARATOR}.+\.rb/
dir = $0
else
dir = File.join(Dir.getwd, $0)
@@ -20,15 +20,19 @@ module PuppetTest
@@basedir = dir
end
end
- @@basedir
+ if list.empty?
+ @@basedir
+ else
+ File.join(@@basedir, *list)
+ end
end
def cleanup(&block)
@@cleaners << block
end
- def datadir
- File.join(basedir, "test", "data")
+ def datadir(*list)
+ File.join(basedir, "test", "data", *list)
end
def exampledir(*args)
@@ -45,6 +49,18 @@ module PuppetTest
module_function :basedir, :datadir, :exampledir
+ # Rails clobbers RUBYLIB, thanks
+ def libsetup
+ curlibs = ENV["RUBYLIB"].split(":")
+ $:.reject do |dir| dir =~ /^\/usr/ end.each do |dir|
+ unless curlibs.include?(dir)
+ curlibs << dir
+ end
+ end
+
+ ENV["RUBYLIB"] = curlibs.join(":")
+ end
+
def rake?
$0 =~ /rake_test_loader/
end
@@ -90,6 +106,7 @@ module PuppetTest
Puppet::Log.level = :debug
#$VERBOSE = 1
Puppet.info @method_name
+ Puppet[:trace] = true
end
#if $0 =~ /.+\.rb/ or Puppet[:debug]
# Puppet::Log.newdestination :console
@@ -169,7 +186,9 @@ module PuppetTest
@@tmppids.clear
Puppet::Type.allclear
Puppet::Storage.clear
- Puppet::Rails.clear
+ if defined? Puppet::Rails
+ Puppet::Rails.clear
+ end
Puppet.clear
@memoryatend = Puppet::Util.memory
diff --git a/test/lib/puppettest/parsertesting.rb b/test/lib/puppettest/parsertesting.rb
index e0606c501..1a6b5b12a 100644
--- a/test/lib/puppettest/parsertesting.rb
+++ b/test/lib/puppettest/parsertesting.rb
@@ -1,4 +1,5 @@
require 'puppettest'
+require 'puppet/rails'
module PuppetTest::ParserTesting
include PuppetTest
@@ -6,23 +7,36 @@ module PuppetTest::ParserTesting
def astarray(*args)
AST::ASTArray.new(
- :children => args
- )
+ :children => args
+ )
end
- def classobj(name, args = {})
- args[:type] ||= nameobj(name)
- args[:code] ||= AST::ASTArray.new(
- :file => __FILE__,
- :line => __LINE__,
- :children => [
- varobj("%svar" % name, "%svalue" % name),
- fileobj("/%s" % name)
- ]
- )
- assert_nothing_raised("Could not create class %s" % name) {
- return AST::ClassDef.new(args)
- }
+ def mkinterp(args = {})
+ args[:Code] ||= ""
+ args[:Local] ||= true
+ Puppet::Parser::Interpreter.new(args)
+ end
+
+ def mkparser
+ Puppet::Parser::Parser.new(mkinterp)
+ end
+
+ def mkscope(hash = {})
+ hash[:interp] ||= mkinterp
+ hash[:source] ||= (hash[:interp].findclass("", "") ||
+ hash[:interp].newclass(""))
+
+ unless hash[:source]
+ raise "Could not find source for scope"
+ end
+ Puppet::Parser::Scope.new(hash)
+ end
+
+ def classobj(name, hash = {})
+ hash[:file] ||= __FILE__
+ hash[:line] ||= __LINE__
+ hash[:type] ||= name
+ AST::HostClass.new(hash)
end
def tagobj(*names)
@@ -40,50 +54,47 @@ module PuppetTest::ParserTesting
}
end
- def compobj(name, args = {})
- args[:file] ||= tempfile()
- args[:line] ||= rand(100)
- args[:type] ||= nameobj(name)
- args[:args] ||= AST::ASTArray.new(
- :file => tempfile(),
- :line => rand(100),
- :children => []
- )
- args[:code] ||= AST::ASTArray.new(
- :file => tempfile(),
- :line => rand(100),
- :children => [
- varobj("%svar" % name, "%svalue" % name),
- fileobj("/%s" % name)
- ]
- )
- assert_nothing_raised("Could not create compdef %s" % name) {
- return AST::CompDef.new(args)
+ def resourcedef(type, title, params)
+ unless title.is_a?(AST)
+ title = stringobj(title)
+ end
+ assert_nothing_raised("Could not create %s %s" % [type, title]) {
+ return AST::ResourceDef.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :title => title,
+ :type => type,
+ :params => resourceinst(params)
+ )
}
end
- def objectdef(type, name, params)
+ def resourceoverride(type, title, params)
assert_nothing_raised("Could not create %s %s" % [type, name]) {
- return AST::ObjectDef.new(
- :file => __FILE__,
- :line => __LINE__,
- :name => stringobj(name),
- :type => nameobj(type),
- :params => objectinst(params)
- )
+ return AST::ResourceOverride.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :object => resourceref(type, title),
+ :type => type,
+ :params => resourceinst(params)
+ )
+ }
+ end
+
+ def resourceref(type, title)
+ assert_nothing_raised("Could not create %s %s" % [type, title]) {
+ return AST::ResourceRef.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :type => type,
+ :title => stringobj(title)
+ )
}
end
def fileobj(path, hash = {"owner" => "root"})
assert_nothing_raised("Could not create file %s" % path) {
- return objectdef("file", path, hash)
- # return AST::ObjectDef.new(
- # :file => tempfile(),
- # :line => rand(100),
- # :name => stringobj(path),
- # :type => nameobj("file"),
- # :params => objectinst(hash)
- # )
+ return resourcedef("file", path, hash)
}
end
@@ -123,12 +134,12 @@ module PuppetTest::ParserTesting
}
end
- def objectinst(hash)
- assert_nothing_raised("Could not create object instance") {
+ def resourceinst(hash)
+ assert_nothing_raised("Could not create resource instance") {
params = hash.collect { |param, value|
- objectparam(param, value)
+ resourceparam(param, value)
}
- return AST::ObjectInst.new(
+ return AST::ResourceInst.new(
:file => tempfile(),
:line => rand(100),
:children => params
@@ -136,16 +147,16 @@ module PuppetTest::ParserTesting
}
end
- def objectparam(param, value)
+ def resourceparam(param, value)
# Allow them to pass non-strings in
if value.is_a?(String)
value = stringobj(value)
end
assert_nothing_raised("Could not create param %s" % param) {
- return AST::ObjectParam.new(
+ return AST::ResourceParam.new(
:file => tempfile(),
:line => rand(100),
- :param => nameobj(param),
+ :param => param,
:value => value
)
}
@@ -194,10 +205,10 @@ module PuppetTest::ParserTesting
def defaultobj(type, params)
pary = []
params.each { |p,v|
- pary << AST::ObjectParam.new(
+ pary << AST::ResourceParam.new(
:file => __FILE__,
:line => __LINE__,
- :param => nameobj(p),
+ :param => p,
:value => stringobj(v)
)
}
@@ -208,10 +219,10 @@ module PuppetTest::ParserTesting
)
assert_nothing_raised("Could not create defaults for %s" % type) {
- return AST::TypeDefaults.new(
+ return AST::ResourceDefaults.new(
:file => __FILE__,
:line => __LINE__,
- :type => typeobj(type),
+ :type => type,
:params => past
)
}
@@ -274,7 +285,7 @@ module PuppetTest::ParserTesting
return obj
end
- def mk_transbucket(*objects)
+ def mk_transbucket(*resources)
bucket = nil
assert_nothing_raised {
bucket = Puppet::TransBucket.new
@@ -282,12 +293,12 @@ module PuppetTest::ParserTesting
bucket.type = "yaytype"
}
- objects.each { |o| bucket << o }
+ resources.each { |o| bucket << o }
return bucket
end
- # Make a tree of objects, yielding if desired
+ # Make a tree of resources, yielding if desired
def mk_transtree(depth = 4, width = 2)
top = nil
assert_nothing_raised {
@@ -300,7 +311,7 @@ module PuppetTest::ParserTesting
file = tempfile()
depth.times do |i|
- objects = []
+ resources = []
width.times do |j|
path = tempfile + i.to_s
obj = Puppet::TransObject.new("file", path)
@@ -312,10 +323,10 @@ module PuppetTest::ParserTesting
yield(obj, i, j)
end
- objects << obj
+ resources << obj
end
- newbucket = mk_transbucket(*objects)
+ newbucket = mk_transbucket(*resources)
bucket.push newbucket
bucket = newbucket
@@ -324,13 +335,13 @@ module PuppetTest::ParserTesting
return top
end
- # Take a list of AST objects, evaluate them, and return the results
+ # Take a list of AST resources, evaluate them, and return the results
def assert_evaluate(children)
top = nil
assert_nothing_raised("Could not create top object") {
top = AST::ASTArray.new(
- :children => children
- )
+ :children => children
+ )
}
trans = nil
diff --git a/test/lib/puppettest/railstesting.rb b/test/lib/puppettest/railstesting.rb
new file mode 100644
index 000000000..3a32d0c9e
--- /dev/null
+++ b/test/lib/puppettest/railstesting.rb
@@ -0,0 +1,34 @@
+module PuppetTest::RailsTesting
+ Parser = Puppet::Parser
+ AST = Puppet::Parser::AST
+ include PuppetTest::ParserTesting
+
+ def railsinit
+ Puppet::Rails.init
+ end
+
+ def railsresource(type = "file", title = "/tmp/testing", params = {})
+ railsinit
+
+ # We need a host for resources
+ host = Puppet::Rails::Host.new(:name => Facter.value("hostname"))
+
+ # Now build a resource
+ resource = host.rails_resources.build(
+ :title => title, :restype => type,
+ :exported => true
+ )
+
+ # Now add some params
+ params.each do |param, value|
+ resource.rails_parameters.build(
+ :name => param, :value => value
+ )
+ end
+
+ # Now save the whole thing
+ host.save
+ end
+end
+
+# $Id$
diff --git a/test/lib/puppettest/resourcetesting.rb b/test/lib/puppettest/resourcetesting.rb
new file mode 100644
index 000000000..a7d183ca7
--- /dev/null
+++ b/test/lib/puppettest/resourcetesting.rb
@@ -0,0 +1,64 @@
+module PuppetTest::ResourceTesting
+ Parser = Puppet::Parser
+ AST = Puppet::Parser::AST
+ def mkclassframing(interp = nil)
+ interp ||= mkinterp
+
+ interp.newdefine("resource", :arguments => [%w{one}, %w{two value}, %w{three}])
+ interp.newclass("")
+ source = interp.newclass("base")
+ interp.newclass("sub1", :parent => "base")
+ interp.newclass("sub2", :parent => "base")
+ interp.newclass("other")
+
+ scope = Parser::Scope.new(:interp => interp)
+ scope.source = source
+
+ return interp, scope, source
+ end
+
+ def mkresource(args = {})
+
+ if args[:scope] and ! args[:source]
+ args[:source] = args[:scope].source
+ end
+
+ unless args[:scope]
+ unless defined? @scope
+ raise "Must set @scope to mkresource"
+ end
+ end
+
+ {:type => "resource", :title => "testing",
+ :source => @source, :scope => @scope}.each do |param, value|
+ args[param] ||= value
+ end
+
+ unless args[:source].is_a?(Puppet::Parser::AST::HostClass)
+ args[:source] = args[:scope].findclass(args[:source])
+ end
+
+ params = args[:params] || {:one => "yay", :three => "rah"}
+ if args[:params] == :none
+ args.delete(:params)
+ else
+ args[:params] = paramify args[:source], params
+ end
+
+ Parser::Resource.new(args)
+ end
+
+ def param(name, value, source)
+ Parser::Resource::Param.new(:name => name, :value => value, :source => source)
+ end
+
+ def paramify(source, hash)
+ hash.collect do |name, value|
+ Parser::Resource::Param.new(
+ :name => name, :value => value, :source => source
+ )
+ end
+ end
+end
+
+# $Id$
diff --git a/test/lib/puppettest/support/utils.rb b/test/lib/puppettest/support/utils.rb
index 00ea1a1c9..ea2d5483c 100644
--- a/test/lib/puppettest/support/utils.rb
+++ b/test/lib/puppettest/support/utils.rb
@@ -102,7 +102,7 @@ module PuppetTest
# a list of files that we can parse for testing
def textfiles
- textdir = File.join(exampledir,"code", "snippets")
+ textdir = datadir "snippets"
Dir.entries(textdir).reject { |f|
f =~ /^\./ or f =~ /fail/
}.each { |f|
@@ -111,7 +111,7 @@ module PuppetTest
end
def failers
- textdir = File.join(exampledir,"code", "failers")
+ textdir = datadir "failers"
# only parse this one file now
files = Dir.entries(textdir).reject { |file|
file =~ %r{\.swp}
diff --git a/test/other/config.rb b/test/other/config.rb
index 0afe8979b..09d6abe3b 100755
--- a/test/other/config.rb
+++ b/test/other/config.rb
@@ -3,9 +3,11 @@
require 'puppet'
require 'puppet/config'
require 'puppettest'
+require 'puppettest/parsertesting'
class TestConfig < Test::Unit::TestCase
include PuppetTest
+ include PuppetTest::ParserTesting
def check_for_users
count = Puppet::Type.type(:user).inject(0) { |c,o|
@@ -39,21 +41,16 @@ class TestConfig < Test::Unit::TestCase
}
Puppet[:parseonly] = true
- parser = Puppet::Parser::Parser.new()
- objects = nil
- assert_nothing_raised("Could not parse generated manifest") {
- parser.string = manifest
- objects = parser.parse
- }
- scope = Puppet::Parser::Scope.new
- assert_nothing_raised("Could not compile objects") {
- scope.evaluate(:ast => objects)
- }
+ interp = nil
+ assert_nothing_raised do
+ interp = mkinterp :Code => manifest, :UseNodes => false
+ end
+
trans = nil
- assert_nothing_raised("Could not convert objects to transportable") {
- trans = scope.to_trans
- }
+ assert_nothing_raised do
+ trans = interp.evaluate(nil, {})
+ end
assert_nothing_raised("Could not instantiate objects") {
trans.to_type
}
diff --git a/test/rails/rails.rb b/test/rails/rails.rb
new file mode 100755
index 000000000..c780d7698
--- /dev/null
+++ b/test/rails/rails.rb
@@ -0,0 +1,87 @@
+#!/usr/bin/ruby
+
+require 'puppet'
+require 'puppet/rails'
+require 'puppet/parser/interpreter'
+require 'puppet/parser/parser'
+require 'puppet/client'
+require 'puppettest'
+require 'puppettest/parsertesting'
+require 'puppettest/resourcetesting'
+require 'puppettest/railstesting'
+
+class TestRails < Test::Unit::TestCase
+ include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
+ include PuppetTest::RailsTesting
+
+ def test_includerails
+ assert_nothing_raised {
+ require 'puppet/rails'
+ }
+ end
+
+ # Don't do any tests w/out this class
+ if defined? ActiveRecord::Base
+ def test_hostcache
+ @interp, @scope, @source = mkclassframing
+ # First make some objects
+ resources = []
+ 20.times { |i|
+ resources << mkresource(:type => "file", :title => "/tmp/file#{i.to_s}",
+ :params => {:owner => "user#{i}"})
+ }
+
+ # Now collect our facts
+ facts = Facter.to_hash
+
+ assert_nothing_raised {
+ Puppet::Rails.init
+ }
+
+ # Now try storing our crap
+ host = nil
+ assert_nothing_raised {
+ host = Puppet::Rails::Host.store(
+ :resources => resources,
+ :facts => facts,
+ :name => facts["hostname"],
+ :classes => ["one", "two::three", "four"]
+ )
+ }
+
+ assert(host, "Did not create host")
+
+ host = nil
+ assert_nothing_raised {
+ host = Puppet::Rails::Host.find_by_name(facts["hostname"])
+ }
+ assert(host, "Could not find host object")
+
+ assert(host.rails_resources, "No objects on host")
+
+ assert_equal(facts["hostname"], host.facts["hostname"],
+ "Did not retrieve facts")
+
+ count = 0
+ host.rails_resources.each do |resource|
+ count += 1
+ i = nil
+ if resource[:title] =~ /file([0-9]+)/
+ i = $1
+ else
+ raise "Got weird resource %s" % resource.inspect
+ end
+
+ assert_equal("user#{i}",
+ resource.rails_parameters.find_by_name("owner")[:value])
+ end
+
+ assert_equal(20, count, "Did not get enough resources")
+ end
+ else
+ $stderr.puts "Install Rails for Rails and Caching tests"
+ end
+end
+
+# $Id$
diff --git a/test/rails/railsparameter.rb b/test/rails/railsparameter.rb
new file mode 100755
index 000000000..7dc7ef5dd
--- /dev/null
+++ b/test/rails/railsparameter.rb
@@ -0,0 +1,50 @@
+#!/usr/bin/ruby
+
+require 'puppet'
+require 'puppet/rails'
+require 'puppettest'
+require 'puppettest/railstesting'
+
+class TestRailsParameter < Test::Unit::TestCase
+ include PuppetTest::RailsTesting
+
+ # Don't do any tests w/out this class
+ if defined? ActiveRecord::Base
+ # Create a resource param from a rails parameter
+ def test_to_resourceparam
+ railsinit
+ # First create our parameter
+ rparam = nil
+ hash = { :name => :myparam, :value => "myval",
+ :file => __FILE__, :line => __LINE__}
+ assert_nothing_raised do
+ rparam = Puppet::Rails::RailsParameter.new(hash)
+ end
+
+ assert(rparam, "Did not create rails parameter")
+
+ # The id doesn't get assigned until we save
+ rparam.save
+
+ # Now create a source
+ interp = mkinterp
+ source = interp.newclass "myclass"
+
+ # And try to convert our parameter
+ pparam = nil
+ assert_nothing_raised do
+ pparam = rparam.to_resourceparam(source)
+ end
+
+
+ assert_instance_of(Puppet::Parser::Resource::Param, pparam)
+ hash.each do |name, value|
+ assert_equal(value, pparam.send(name), "%s was not equal" % name)
+ end
+ end
+ else
+ $stderr.puts "Install Rails for Rails and Caching tests"
+ end
+end
+
+# $Id$
diff --git a/test/rails/railsresource.rb b/test/rails/railsresource.rb
new file mode 100755
index 000000000..86c3b4908
--- /dev/null
+++ b/test/rails/railsresource.rb
@@ -0,0 +1,60 @@
+#!/usr/bin/ruby
+
+require 'puppet'
+require 'puppet/rails'
+require 'puppettest'
+require 'puppettest/railstesting'
+require 'puppettest/resourcetesting'
+
+class TestRailsResource < Test::Unit::TestCase
+ include PuppetTest::RailsTesting
+ include PuppetTest::ResourceTesting
+
+ # Don't do any tests w/out this class
+ if defined? ActiveRecord::Base
+ # Create a resource param from a rails parameter
+ def test_to_resource
+ railsinit
+
+ # We need a host for resources
+ host = Puppet::Rails::Host.new(:name => "myhost")
+
+ # Now build a resource
+ resource = host.rails_resources.build(
+ :title => "/tmp/to_resource", :restype => "file",
+ :exported => true
+ )
+
+ # Now add some params
+ {"owner" => "root", "mode" => "644"}.each do |param, value|
+ resource.rails_parameters.build(
+ :name => param, :value => value
+ )
+ end
+
+ # Now save the whole thing
+ host.save
+
+ # Now, try to convert our resource to a real resource
+
+ # We need a scope
+ interp, scope, source = mkclassframing
+
+ res = nil
+ assert_nothing_raised do
+ res = resource.to_resource(scope)
+ end
+
+ assert_instance_of(Puppet::Parser::Resource, res)
+
+ assert_equal("root", res[:owner])
+ assert_equal("644", res[:mode])
+ assert_equal("/tmp/to_resource", res.title)
+ assert_equal(source, res.source)
+ end
+ else
+ $stderr.puts "Install Rails for Rails and Caching tests"
+ end
+end
+
+# $Id$
diff --git a/test/tagging/tagging.rb b/test/tagging/tagging.rb
index 42f04ff4f..b30160bcb 100644
--- a/test/tagging/tagging.rb
+++ b/test/tagging/tagging.rb
@@ -1,14 +1,18 @@
require 'puppet'
require 'puppettest'
+require 'puppettest/parsertesting'
+require 'puppettest/resourcetesting'
class TestTagging < Test::Unit::TestCase
include PuppetTest
+ include PuppetTest::ParserTesting
+ include PuppetTest::ResourceTesting
# Make sure the scopes are getting the right tags
def test_scopetags
scope = nil
assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
scope.name = "yayness"
scope.type = "solaris"
}
@@ -22,7 +26,7 @@ class TestTagging < Test::Unit::TestCase
def test_deepscopetags
scope = nil
assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
scope.name = "yayness"
scope.type = "solaris"
scope = scope.newscope
@@ -40,36 +44,21 @@ class TestTagging < Test::Unit::TestCase
def test_objecttags
scope = nil
assert_nothing_raised {
- scope = Puppet::Parser::Scope.new()
+ scope = mkscope
scope.name = "yayness"
scope.type = "solaris"
}
- assert_nothing_raised {
- scope.setobject(
- :type => "file",
- :name => "/etc/passwd",
- :arguments => {"owner" => "root"},
- :file => "/yay",
- :line => 1
- )
- }
+ resource = mkresource :type => "file", :title => "/tmp/testing",
+ :params => {:owner => "root"}, :file => "/yay", :line => 1,
+ :scope => scope
- ast = Puppet::Parser::AST::ASTArray.new({})
-
- # We have to use 'evaluate', rather than just calling to_trans directly,
- # because scopes do some internal checking to make sure the same object
- # is not translated multiple times.
- objects = nil
assert_nothing_raised {
- objects = scope.evaluate(:ast => ast)
+ scope.setresource(resource)
}
- # There's only one object, so shift it out
- object = objects.shift
-
assert_nothing_raised {
- assert_equal(%w{solaris}, object.tags,
+ assert_equal(%w{solaris}, resource.tags,
"Incorrect tags")
}
end