diff --git a/lib/puppet/application/main.rb b/lib/puppet/application/main.rb index 3813df612..2952ef4ae 100644 --- a/lib/puppet/application/main.rb +++ b/lib/puppet/application/main.rb @@ -1,183 +1,183 @@ require 'puppet' require 'puppet/application' require 'puppet/configurer' require 'puppet/network/handler' require 'puppet/network/client' Puppet::Application.new(:main) do should_parse_config option("--debug","-d") option("--execute EXECUTE","-e") do |arg| options[:code] = arg end option("--loadclasses","-L") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") option("--apply catalog", "-a catalog") do |arg| options[:catalog] = arg end option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end dispatch do if options[:catalog] :apply elsif Puppet[:parseonly] :parseonly else :main end end command(:apply) do require 'puppet/configurer' if options[:catalog] == "-" text = $stdin.read else text = File.read(options[:catalog]) end begin catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) unless catalog.is_a?(Puppet::Resource::Catalog) catalog = Puppet::Resource::Catalog.pson_create(catalog) end rescue => detail raise Puppet::Error, "Could not deserialize catalog from pson: %s" % detail end catalog = catalog.to_ral configurer = Puppet::Configurer.new configurer.run :catalog => catalog end command(:parseonly) do # Set our code or file to use. if options[:code] or ARGV.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = ARGV.shift end begin - Puppet::Parser::ResourceTypeCollection.new(Puppet[:environment]).perform_initial_import + Puppet::Resource::TypeCollection.new(Puppet[:environment]).perform_initial_import rescue => detail Puppet.err detail exit 1 end exit 0 end command(:main) do # Set our code or file to use. if options[:code] or ARGV.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = ARGV.shift end # Collect our facts. unless facts = Puppet::Node::Facts.find(Puppet[:certname]) raise "Could not find facts for %s" % Puppet[:certname] end # Find our Node unless node = Puppet::Node.find(Puppet[:certname]) raise "Could not find node %s" % Puppet[:certname] end # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppetd creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "%s is not readable" % file exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog starttime = Time.now catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral catalog.host_config = true if Puppet[:graph] or Puppet[:report] catalog.finalize catalog.retrieval_duration = Time.now - starttime configurer = Puppet::Configurer.new configurer.execute_prerun_command # And apply it transaction = catalog.apply configurer.execute_postrun_command status = 0 if not Puppet[:noop] and options[:detailed_exitcodes] then transaction.generate_report exit(transaction.report.exit_status) else exit(0) end rescue => detail puts detail.backtrace if Puppet[:trace] if detail.is_a?(XMLRPC::FaultException) $stderr.puts detail.message else $stderr.puts detail end exit(1) end end setup do if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end # If noop is set, then also enable diffs if Puppet[:noop] Puppet[:show_diff] = true end unless options[:logset] Puppet::Util::Log.newdestination(:console) end client = nil server = nil trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/server.rb b/lib/puppet/application/server.rb index e9253c6f9..afdad54fb 100644 --- a/lib/puppet/application/server.rb +++ b/lib/puppet/application/server.rb @@ -1,168 +1,168 @@ require 'puppet' require 'puppet/application' require 'puppet/daemon' require 'puppet/network/server' require 'puppet/network/http/rack' if Puppet.features.rack? Puppet::Application.new(:server) do should_parse_config option("--debug", "-d") option("--verbose", "-v") # internal option, only to be used by ext/rack/config.ru option("--rack") option("--compile host", "-c host") do |arg| options[:node] = arg end option("--logdest DEST", "-l DEST") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail if Puppet[:debug] puts detail.backtrace end $stderr.puts detail.to_s end end preinit do trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end # Create this first-off, so we have ARGV @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup end dispatch do if options[:node] :compile elsif Puppet[:parseonly] :parseonly else :main end end command(:compile) do Puppet::Util::Log.newdestination :console raise ArgumentError, "Cannot render compiled catalogs without pson support" unless Puppet.features.pson? begin unless catalog = Puppet::Resource::Catalog.find(options[:node]) raise "Could not compile catalog for %s" % options[:node] end $stdout.puts catalog.render(:pson) rescue => detail $stderr.puts detail exit(30) end exit(0) end command(:parseonly) do begin - Puppet::Parser::ResourceTypeCollection.new(Puppet[:environment]).perform_initial_import + Puppet::Resource::TypeCollection.new(Puppet[:environment]).perform_initial_import rescue => detail Puppet.err detail exit 1 end exit(0) end command(:main) do require 'etc' require 'puppet/file_serving/content' require 'puppet/file_serving/metadata' require 'puppet/checksum' xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket] if Puppet[:ca] xmlrpc_handlers << :CA end # Make sure we've got a localhost ssl cert Puppet::SSL::Host.localhost # And now configure our server to *only* hit the CA for data, because that's # all it will have write access to. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :only end if Process.uid == 0 begin Puppet::Util.chuser rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not change user to %s: %s" % [Puppet[:user], detail] exit(39) end end unless options[:rack] @daemon.server = Puppet::Network::Server.new(:xmlrpc_handlers => xmlrpc_handlers) @daemon.daemonize if Puppet[:daemonize] else require 'puppet/network/http/rack' @app = Puppet::Network::HTTP::Rack.new(:xmlrpc_handlers => xmlrpc_handlers, :protocols => [:rest, :xmlrpc]) end Puppet.notice "Starting Puppet server version %s" % [Puppet.version] unless options[:rack] @daemon.start else return @app end end setup do # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end unless Puppet[:daemonize] or options[:rack] Puppet::Util::Log.newdestination(:console) options[:setdest] = true end end unless options[:setdest] Puppet::Util::Log.newdestination(:syslog) end if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end Puppet.settings.use :main, :puppetmasterd, :ssl # A temporary solution, to at least make the master work for now. Puppet::Node::Facts.terminus_class = :yaml # Cache our nodes in yaml. Currently not configurable. Puppet::Node.cache_class = :yaml # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :local Puppet.settings.use :ca Puppet::SSL::CertificateAuthority.instance else Puppet::SSL::Host.ca_location = :none end end end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index a6e33703b..537a5dd26 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -1,104 +1,104 @@ require 'puppet/util/cacher' # Just define it, so this class has fewer load dependencies. class Puppet::Node end # Model the environment that a node can operate in. This class just # provides a simple wrapper for the functionality around environments. class Puppet::Node::Environment include Puppet::Util::Cacher @seen = {} # Return an existing environment instance, or create a new one. def self.new(name = nil) name ||= Puppet.settings.value(:environment) raise ArgumentError, "Environment name must be specified" unless name symbol = name.to_sym return @seen[symbol] if @seen[symbol] obj = self.allocate obj.send :initialize, symbol @seen[symbol] = obj end # This is only used for testing. def self.clear @seen.clear end attr_reader :name # Return an environment-specific setting. def [](param) Puppet.settings.value(param, self.name) end def initialize(name) @name = name end def known_resource_types if @known_resource_types.nil? or @known_resource_types.stale? - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new(self) + @known_resource_types = Puppet::Resource::TypeCollection.new(self) @known_resource_types.perform_initial_import end @known_resource_types end def module(name) mod = Puppet::Module.new(name, self) return nil unless mod.exist? return mod end # Cache the modulepath, so that we aren't searching through # all known directories all the time. cached_attr(:modulepath, :ttl => Puppet[:filetimeout]) do dirs = self[:modulepath].split(File::PATH_SEPARATOR) if ENV["PUPPETLIB"] dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs end validate_dirs(dirs) end # Return all modules from this environment. # Cache the list, because it can be expensive to create. cached_attr(:modules, :ttl => Puppet[:filetimeout]) do module_names = modulepath.collect { |path| Dir.entries(path) }.flatten.uniq module_names.collect do |path| begin Puppet::Module.new(path, self) rescue Puppet::Module::Error => e nil end end.compact end # Cache the manifestdir, so that we aren't searching through # all known directories all the time. cached_attr(:manifestdir, :ttl => Puppet[:filetimeout]) do validate_dirs(self[:manifestdir].split(File::PATH_SEPARATOR)) end def to_s name.to_s end def validate_dirs(dirs) dirs.collect do |dir| if dir !~ /^#{File::SEPARATOR}/ File.join(Dir.getwd, dir) else dir end end.find_all do |p| p =~ /^#{File::SEPARATOR}/ && FileTest.directory?(p) end end end diff --git a/lib/puppet/parser.rb b/lib/puppet/parser.rb index 3eda73885..4d274b43c 100644 --- a/lib/puppet/parser.rb +++ b/lib/puppet/parser.rb @@ -1,4 +1,4 @@ require 'puppet/parser/parser' require 'puppet/parser/compiler' -require 'puppet/parser/resource_type_collection' +require 'puppet/resource/type_collection' diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 25b064195..c669076bb 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -1,410 +1,410 @@ # Created by Luke A. Kanies on 2007-08-13. # Copyright (c) 2007. All rights reserved. require 'puppet/node' require 'puppet/resource/catalog' require 'puppet/util/errors' -require 'puppet/parser/resource_type_collection_helper' +require 'puppet/resource/type_collection_helper' # Maintain a graph of scopes, along with a bunch of data # about the individual catalog we're compiling. class Puppet::Parser::Compiler include Puppet::Util include Puppet::Util::Errors - include Puppet::Parser::ResourceTypeCollectionHelper + include Puppet::Resource::TypeCollectionHelper def self.compile(node) new(node).compile.to_resource rescue => detail puts detail.backtrace if Puppet[:trace] raise Puppet::Error, "#{detail} on node #{node.name}" end attr_reader :node, :facts, :collections, :catalog, :node_scope, :resources # Add a collection to the global list. def add_collection(coll) @collections << coll end # Store a resource override. def add_override(override) # If possible, merge the override in immediately. if resource = @catalog.resource(override.ref) resource.merge(override) else # Otherwise, store the override for later; these # get evaluated in Resource#finish. @resource_overrides[override.ref] << override end end # Store a resource in our resource table. def add_resource(scope, resource) @resources << resource # Note that this will fail if the resource is not unique. @catalog.add_resource(resource) # And in the resource graph. At some point, this might supercede # the global resource table, but the table is a lot faster # so it makes sense to maintain for now. if resource.type.to_s.downcase == "class" and main = @catalog.resource(:class, :main) @catalog.add_edge(main, resource) else @catalog.add_edge(scope.resource, resource) end end # Do we use nodes found in the code, vs. the external node sources? def ast_nodes? known_resource_types.nodes? end # Store the fact that we've evaluated a class def add_class(name) @catalog.add_class(name) unless name == "" end # Return a list of all of the defined classes. def classlist return @catalog.classes end # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile # Set the client's parameters into the top scope. set_node_parameters() evaluate_main() evaluate_ast_node() evaluate_node_classes() evaluate_generators() finish() fail_on_unevaluated() return @catalog end # LAK:FIXME There are no tests for this. def delete_collection(coll) @collections.delete(coll) if @collections.include?(coll) end # Return the node's environment. def environment unless defined? @environment if node.environment and node.environment != "" @environment = node.environment else @environment = nil end end @environment end # Evaluate all of the classes specified by the node. def evaluate_node_classes evaluate_classes(@node.classes, topscope) end # Evaluate each specified class in turn. If there are any classes we can't # find, just tag the catalog and move on. This method really just # creates resource objects that point back to the classes, and then the # resources are themselves evaluated later in the process. def evaluate_classes(classes, scope, lazy_evaluate = true) unless scope.source raise Puppet::DevError, "No source for scope passed to evaluate_classes" end found = [] classes.each do |name| # If we can find the class, then make a resource that will evaluate it. if klass = scope.find_hostclass(name) found << name and next if scope.class_scope(klass) resource = klass.mk_plain_resource(scope) # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. resource.evaluate unless lazy_evaluate found << name else Puppet.info "Could not find class %s for %s" % [name, node.name] @catalog.tag(name) end end found end # Return a resource by either its ref or its type and title. def findresource(*args) @catalog.resource(*args) end def initialize(node, options = {}) @node = node options.each do |param, value| begin send(param.to_s + "=", value) rescue NoMethodError raise ArgumentError, "Compiler objects do not accept %s" % param end end initvars() init_main() end # Create a new scope, with either a specified parent scope or # using the top scope. def newscope(parent, options = {}) parent ||= topscope options[:compiler] = self scope = Puppet::Parser::Scope.new(options) scope.parent = parent scope end # Return any overrides for the given resource. def resource_overrides(resource) @resource_overrides[resource.ref] end # The top scope is usually the top-level scope, but if we're using AST nodes, # then it is instead the node's scope. def topscope node_scope || @topscope end private # If ast nodes are enabled, then see if we can find and evaluate one. def evaluate_ast_node return unless ast_nodes? # Now see if we can find the node. astnode = nil @node.names.each do |name| break if astnode = known_resource_types.node(name.to_s.downcase) end unless (astnode ||= known_resource_types.node("default")) raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") end # Create a resource to model this node, and then add it to the list # of resources. resource = astnode.mk_plain_resource(topscope) resource.evaluate # Now set the node scope appropriately, so that :topscope can # behave differently. @node_scope = topscope.class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. # The 'true' is used to continue a loop, so it's important. def evaluate_collections return false if @collections.empty? found_something = false exceptwrap do # We have to iterate over a dup of the array because # collections can delete themselves from the list, which # changes its length and causes some collections to get missed. @collections.dup.each do |collection| found_something = true if collection.evaluate end end return found_something end # Make sure all of our resources have been evaluated into native resources. # We return true if any resources have, so that we know to continue the # evaluate_generators loop. def evaluate_definitions exceptwrap do if ary = unevaluated_resources evaluated = false ary.each do |resource| if not resource.virtual? resource.evaluate evaluated = true end end # If we evaluated, let the loop know. return evaluated else return false end end end # Iterate over collections and resources until we're sure that the whole # compile is evaluated. This is necessary because both collections # and defined resources can generate new resources, which themselves could # be defined resources. def evaluate_generators count = 0 loop do done = true # Call collections first, then definitions. done = false if evaluate_collections done = false if evaluate_definitions break if done count += 1 if count > 1000 raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" end end end # Find and evaluate our main object, if possible. def evaluate_main - @main = known_resource_types.find_hostclass("", "") || known_resource_types.add(Puppet::Parser::ResourceType.new(:hostclass, "")) + @main = known_resource_types.find_hostclass("", "") || known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) @topscope.source = @main @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) @topscope.resource = @main_resource @resources << @main_resource @catalog.add_resource(@main_resource) @main_resource.evaluate end # Make sure the entire catalog is evaluated. def fail_on_unevaluated fail_on_unevaluated_overrides fail_on_unevaluated_resource_collections end # If there are any resource overrides remaining, then we could # not find the resource they were supposed to override, so we # want to throw an exception. def fail_on_unevaluated_overrides remaining = [] @resource_overrides.each do |name, overrides| remaining += overrides end unless remaining.empty? fail Puppet::ParseError, "Could not find resource(s) %s for overriding" % remaining.collect { |o| o.ref }.join(", ") end end # Make sure we don't have any remaining collections that specifically # look for resources, because we want to consider those to be # parse errors. def fail_on_unevaluated_resource_collections remaining = [] @collections.each do |coll| # We're only interested in the 'resource' collections, # which result from direct calls of 'realize'. Anything # else is allowed not to return resources. # Collect all of them, so we have a useful error. if r = coll.resources if r.is_a?(Array) remaining += r else remaining << r end end end unless remaining.empty? raise Puppet::ParseError, "Failed to realize virtual resources %s" % remaining.join(', ') end end # Make sure all of our resources and such have done any last work # necessary. def finish resources.each do |resource| # Add in any resource overrides. if overrides = resource_overrides(resource) overrides.each do |over| resource.merge(over) end # Remove the overrides, so that the configuration knows there # are none left. overrides.clear end resource.finish if resource.respond_to?(:finish) end end # Initialize the top-level scope, class, and resource. def init_main # Create our initial scope and a resource that will evaluate main. @topscope = Puppet::Parser::Scope.new(:compiler => self) end # Set up all of our internal variables. def initvars # The list of objects that will available for export. @exported_resources = {} # The list of overrides. This is used to cache overrides on objects # that don't exist yet. We store an array of each override. @resource_overrides = Hash.new do |overs, ref| overs[ref] = [] end # The list of collections that have been created. This is a global list, # but they each refer back to the scope that created them. @collections = [] # For maintaining the relationship between scopes and their resources. @catalog = Puppet::Resource::Catalog.new(@node.name) @catalog.version = known_resource_types.version # local resource array to maintain resource ordering @resources = [] # Make sure any external node classes are in our class list @catalog.add_class(*@node.classes) end # Set the node's parameters into the top-scope as variables. def set_node_parameters node.parameters.each do |param, value| @topscope.setvar(param, value) end # These might be nil. catalog.client_version = node.parameters["clientversion"] catalog.server_version = node.parameters["serverversion"] end # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources ary = resources.reject { |resource| resource.builtin? or resource.evaluated? } if ary.empty? return nil else return ary end end end diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index b21ead3af..e6bc46b86 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -1,402 +1,402 @@ # I pulled this into a separate file, because I got # tired of rebuilding the parser.rb file all the time. class Puppet::Parser::Parser require 'puppet/parser/functions' require 'puppet/parser/files' - require 'puppet/parser/resource_type_collection' - require 'puppet/parser/resource_type_collection_helper' - require 'puppet/parser/resource_type' + require 'puppet/resource/type_collection' + require 'puppet/resource/type_collection_helper' + require 'puppet/resource/type' require 'monitor' AST = Puppet::Parser::AST - include Puppet::Parser::ResourceTypeCollectionHelper + include Puppet::Resource::TypeCollectionHelper attr_reader :version, :environment attr_accessor :files attr_accessor :lexer # 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) result = args.shift args.each { |arg| result.push arg } else result = ast AST::ASTArray, :children => args end return result end # Create an AST object, and automatically add the file and line information if # available. def ast(klass, hash = {}) klass.new ast_context(klass.use_docs).merge(hash) end def ast_context(include_docs = false) result = { :line => lexer.line, :file => lexer.file } result[:doc] = lexer.getcomment(result[:line]) if include_docs result end # The fully qualifed name, with the full namespace. def classname(name) [@lexer.namespace, name].join("::").sub(/^::/, '') end def clear initvars end # Raise a Parse error. def error(message) if brace = @lexer.expected message += "; expected '%s'" end except = Puppet::ParseError.new(message) except.line = @lexer.line if @lexer.file except.file = @lexer.file end raise except end def file @lexer.file end def file=(file) unless FileTest.exist?(file) unless file =~ /\.pp$/ file = file + ".pp" end unless FileTest.exist?(file) raise Puppet::Error, "Could not find file %s" % file end end raise Puppet::AlreadyImportedError, "Import loop detected" if known_resource_types.watching_file?(file) watch_file(file) @lexer.file = file end [:hostclass, :definition, :node, :nodes?].each do |method| define_method(method) do |*args| known_resource_types.send(method, *args) end end def find_hostclass(namespace, name) find_or_load(namespace, name, :hostclass) end def find_definition(namespace, name) find_or_load(namespace, name, :definition) end def find_or_load(namespace, name, type) method = "find_#{type}" namespace = namespace.downcase name = name.downcase fullname = (namespace + "::" + name).sub(/^::/, '') if name =~ /^::/ names_to_try = [name.sub(/^::/, '')] else names_to_try = [fullname] # Try to load the module init file if we're a qualified name names_to_try << fullname.split("::")[0] if fullname.include?("::") # Otherwise try to load the bare name on its own. This # is appropriate if the class we're looking for is in a # module that's different from our namespace. names_to_try << name names_to_try.compact! end until (result = known_resource_types.send(method, namespace, name)) or names_to_try.empty? do self.load(names_to_try.shift) end return result end # Import our files. def import(file) if Puppet[:ignoreimport] return AST::ASTArray.new(:children => []) end # use a path relative to the file doing the importing if @lexer.file dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '') else dir = "." end if dir == "" dir = "." end result = ast AST::ASTArray # We can't interpolate at this point since we don't have any # scopes set up. Warn the user if they use a variable reference raise "Got no file" unless file pat = file if pat.index("$") Puppet.warning( "The import of #{pat} contains a variable reference;" + " variables are not interpolated for imports " + "in file #{@lexer.file} at line #{@lexer.line}" ) end files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => @environment) if files.size == 0 raise Puppet::ImportError.new("No file(s) found for import " + "of '#{pat}'") end files.collect { |file| parser = Puppet::Parser::Parser.new(@environment) parser.files = self.files Puppet.debug("importing '%s'" % file) unless file =~ /^#{File::SEPARATOR}/ file = File.join(dir, file) end begin parser.file = file rescue Puppet::AlreadyImportedError # This file has already been imported to just move on next end # This will normally add code to the 'main' class. parser.parse } end def initialize(env) # The environment is needed to know how to find the resource type collection. @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env initvars() end # Initialize or reset all of our variables. def initvars @lexer = Puppet::Parser::Lexer.new() @files = {} @loaded = [] @loading = {} @loading.extend(MonitorMixin) class << @loading def done_with(item) synchronize do delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current end end def owner_of(item) synchronize do if !self.has_key? item self[item] = { :loader => Thread.current, :busy => self.new_cond} :nobody elsif self[item][:loader] == Thread.current :this_thread else flag = self[item][:busy] flag.wait flag.signal :another_thread end end end end end # Utility method factored out of load def able_to_import?(classname,item,msg) unless @loaded.include?(item) begin case @loading.owner_of(item) when :this_thread return when :another_thread return able_to_import?(classname,item,msg) when :nobody import(item) Puppet.info "Autoloaded #{msg}" @loaded << item end rescue Puppet::ImportError => detail # We couldn't load the item ensure @loading.done_with(item) end end # We don't know whether we're looking for a class or definition, so we have # to test for both. return known_resource_types.hostclass(classname) || known_resource_types.definition(classname) end # Try to load a class, since we could not find it. def load(classname) return false if classname == "" filename = classname.gsub("::", File::SEPARATOR) mod = filename.scan(/^[\w-]+/).shift # First try to load the top-level module then the individual file [[mod, "module %s" % mod ], [filename,"file %s from module %s" % [filename, mod]] ].any? { |item,description| able_to_import?(classname,item,description) } 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 # Create a new class, or merge with an existing class. def newclass(name, options = {}) - known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, name, ast_context(true).merge(options)) + known_resource_types.add Puppet::Resource::Type.new(:hostclass, name, ast_context(true).merge(options)) end # Create a new definition. def newdefine(name, options = {}) - known_resource_types.add Puppet::Parser::ResourceType.new(:definition, name, ast_context(true).merge(options)) + known_resource_types.add Puppet::Resource::Type.new(:definition, name, ast_context(true).merge(options)) 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) context = ast_context(true) names.collect do |name| - known_resource_types.add(Puppet::Parser::ResourceType.new(:node, name, context.merge(options))) + known_resource_types.add(Puppet::Resource::Type.new(:node, name, context.merge(options))) end end def on_error(token,value,stack) if token == 0 # denotes end of file value = 'end of file' else value = "'%s'" % value[:value] end error = "Syntax error at %s" % [value] if brace = @lexer.expected error += "; expected '%s'" % brace end except = Puppet::ParseError.new(error) except.line = @lexer.line if @lexer.file except.file = @lexer.file end raise except end # how should I do error handling here? def parse(string = nil) return parse_ruby_file if self.file =~ /\.rb$/ if string self.string = string end begin @yydebug = false main = yyparse(@lexer,:scan) rescue Racc::ParseError => except error = Puppet::ParseError.new(except) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace raise error rescue Puppet::ParseError => except except.line ||= @lexer.line except.file ||= @lexer.file raise except rescue Puppet::Error => except # and this is a framework error except.line ||= @lexer.line except.file ||= @lexer.file raise except rescue Puppet::DevError => except except.line ||= @lexer.line except.file ||= @lexer.file raise except rescue => except error = Puppet::DevError.new(except.message) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace raise error end if main # Store the results as the top-level class. newclass("", :code => main) end return known_resource_types ensure @lexer.clear end def parse_ruby_file require self.file end # See if any of the files have changed. def reparse? if file = @files.detect { |name, file| file.changed? } return file[1].stamp else return false end end def string=(string) @lexer.string = string end def version known_resource_types.version end # Add a new file to be checked when we're checking to see if we should be # reparsed. This is basically only used by the TemplateWrapper to let the # parser know about templates that should be parsed. def watch_file(filename) known_resource_types.watch_file(filename) end private def check_and_add_to_watched_files(filename) unless @files.include?(filename) @files[filename] = Puppet::Util::LoadedFile.new(filename) return true else return false end end end diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb index bb50efdce..ac1c79aef 100644 --- a/lib/puppet/parser/resource/reference.rb +++ b/lib/puppet/parser/resource/reference.rb @@ -1,103 +1,103 @@ # A reference to a resource. Mostly just the type and title. require 'puppet/resource/reference' require 'puppet/file_collection/lookup' require 'puppet/parser/yaml_trimmer' -require 'puppet/parser/resource_type_collection_helper' +require 'puppet/resource/type_collection_helper' # A reference to a resource. Mostly just the type and title. class Puppet::Parser::Resource::Reference < Puppet::Resource::Reference include Puppet::Parser::YamlTrimmer include Puppet::FileCollection::Lookup include Puppet::Util::MethodHelper include Puppet::Util::Errors - include Puppet::Parser::ResourceTypeCollectionHelper + include Puppet::Resource::TypeCollectionHelper attr_accessor :builtin, :file, :line, :scope # Are we a builtin type? def builtin? unless defined? @builtin if builtintype() @builtin = true else @builtin = false end end @builtin end def builtintype if t = Puppet::Type.type(self.type.downcase) and t.name != :component t else nil end end # Return the defined type for our obj. This can return classes, # definitions or nodes. def definedtype unless defined? @definedtype case self.type when "Class" # look for host classes name = self.title == :main ? "" : self.title unless tmp = known_resource_types.find_hostclass("", name) fail Puppet::ParseError, "Could not find '#{title}' class" end when "Node" # look for node definitions unless tmp = known_resource_types.node(self.title) fail Puppet::ParseError, "Could not find node '%s'" % self.title end else # normal definitions # The resource type is capitalized, so we have to downcase. Really, # we should have a better interface for finding these, but eh. tmp = known_resource_types.definition(self.type.downcase) end if tmp @definedtype = tmp else fail Puppet::ParseError, "Could not find resource type '%s'" % self.type end end @definedtype end def environment scope.environment end def initialize(hash) set_options(hash) requiredopts(:type, :title) end def skip_for_yaml %w{@typeclass @definedtype} end def to_ref # We have to return different cases to provide backward compatibility # from 0.24.x to 0.23.x. if builtin? return [type.to_s.downcase, title.to_s] else return [type.to_s, title.to_s] end 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 diff --git a/lib/puppet/parser/resource_type_collection_helper.rb b/lib/puppet/parser/resource_type_collection_helper.rb deleted file mode 100644 index 51ccdc061..000000000 --- a/lib/puppet/parser/resource_type_collection_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'puppet/parser/resource_type_collection' - -module Puppet::Parser::ResourceTypeCollectionHelper - def known_resource_types - environment.known_resource_types - end -end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 1f7fd7188..b79b344e3 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -1,454 +1,454 @@ # The scope class, which handles storing and retrieving variables and types and # such. require 'puppet/parser/parser' require 'puppet/parser/templatewrapper' require 'puppet/transportable' require 'strscan' -require 'puppet/parser/resource_type_collection_helper' +require 'puppet/resource/type_collection_helper' class Puppet::Parser::Scope - include Puppet::Parser::ResourceTypeCollectionHelper + include Puppet::Resource::TypeCollectionHelper require 'puppet/parser/resource' AST = Puppet::Parser::AST Puppet::Util.logmethods(self) include Enumerable include Puppet::Util::Errors attr_accessor :level, :source, :resource attr_accessor :base, :keyword, :nodescope attr_accessor :top, :translated, :compiler attr_accessor :parent # A demeterific shortcut to the catalog. def catalog compiler.catalog end def environment compiler.environment end # Proxy accessors def host @compiler.node.name 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 == "" or value == :undef return false else return true end end # Is the value a number?, return the correct object or nil if not a number def self.number?(value) unless value.is_a?(Fixnum) or value.is_a?(Bignum) or value.is_a?(Float) or value.is_a?(String) return nil end if value.is_a?(String) if value =~ /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/ return value.to_f elsif value =~ /^0x[0-9a-f]+$/i return value.to_i(16) elsif value =~ /^0[0-7]+$/ return value.to_i(8) elsif value =~ /^-?\d+$/ return value.to_i else return nil end end # it is one of Fixnum,Bignum or Float return value end # Add to our list of namespaces. def add_namespace(ns) return false if @namespaces.include?(ns) if @namespaces == [""] @namespaces = [ns] else @namespaces << ns end end # Are we the top scope? def topscope? @level == 1 end def find_hostclass(name) @namespaces.each do |namespace| if r = known_resource_types.find_hostclass(namespace, name) return r end end return nil end def find_definition(name) @namespaces.each do |namespace| if r = known_resource_types.find_definition(namespace, name) return r end end return nil end def findresource(string, name = nil) compiler.findresource(string, name) end # Initialize our new scope. Defaults to having no parent. def initialize(hash = {}) if hash.include?(:namespace) if n = hash[:namespace] @namespaces = [n] end hash.delete(:namespace) else @namespaces = [""] end 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 = [] # The symbol table for this scope. This is where we store variables. @symtable = {} # the ephemeral symbol tables # those should not persist long, and are used for the moment only # for $0..$xy capture variables of regexes @ephemeral = {} # 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. @defaults = Hash.new { |dhash,type| dhash[type] = {} } # The table for storing class singletons. This will only actually # be used by top scopes and node scopes. @class_scopes = {} end # Store the fact that we've evaluated a class, and store a reference to # the scope in which it was evaluated, so that we can look it up later. def class_set(name, scope) return parent.class_set(name,scope) if parent if existing = @class_scopes[name] if existing.nodescope? != scope.nodescope? raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" else raise Puppet::DevError, "Somehow evaluated %s %s twice" % [ existing.nodescope? ? "node" : "class", name] end end @class_scopes[name] = scope end # Return the scope associated with a class. This is just here so # that subclasses can set their parent scopes to be the scope of # their parent class, and it's also used when looking up qualified # variables. def class_scope(klass) # They might pass in either the class or class name k = klass.respond_to?(:name) ? klass.name : klass @class_scopes[k] || (parent && parent.class_scope(k)) 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 @defaults.include?(type) @defaults[type].each { |var,value| values[var] = value } end #Puppet.debug "Got defaults for %s: %s" % # [type,values.inspect] return values end # Look up a defined type. def lookuptype(name) find_definition(name) || find_hostclass(name) end def lookup_qualified_var(name, usestring) parts = name.split(/::/) shortname = parts.pop klassname = parts.join("::") klass = find_hostclass(klassname) unless klass warning "Could not look up qualified variable '%s'; class %s could not be found" % [name, klassname] return usestring ? "" : :undefined end unless kscope = class_scope(klass) warning "Could not look up qualified variable '%s'; class %s has not been evaluated" % [name, klassname] return usestring ? "" : :undefined end return kscope.lookupvar(shortname, usestring) end private :lookup_qualified_var # 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) table = ephemeral?(name) ? @ephemeral : @symtable # If the variable is qualified, then find the specified scope and look the variable up there instead. if name =~ /::/ return lookup_qualified_var(name, usestring) end # We can't use "if table[name]" here because the value might be false if table.include?(name) if usestring and table[name] == :undef return "" else return table[name] end elsif self.parent return parent.lookupvar(name, usestring) elsif usestring return "" else return :undefined end end # Return a hash containing our variables and their values, optionally (and # by default) including the values defined in our parent. Local values # shadow parent values. def to_hash(recursive = true) if recursive and parent then target = parent.to_hash(recursive) end target ||= Hash.new @symtable.keys.each { |name| value = @symtable[name] if value == :undef then target.delete(name) else target[name] = value end } return target end def namespaces @namespaces.dup end # Create a new scope and set these options. def newscope(options = {}) compiler.newscope(self, options) end # Is this class for a node? This is used to make sure that # nodes and classes with the same name conflict (#620), which # is required because of how often the names are used throughout # the system, including on the client. def nodescope? self.nodescope end # Return the list of scopes up to the top scope, ordered with our own first. # This is used for looking up variables and defaults. def scope_path if parent [self, parent.scope_path].flatten.compact else [self] end end # 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 = @defaults[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 table.include?(param.name) raise Puppet::ParseError.new("Default already defined for %s { %s }; cannot redefine" % [type, param.name], param.line, param.file) end table[param.name] = param } 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. def setvar(name,value, options = {}) table = options[:ephemeral] ? @ephemeral : @symtable #Puppet.debug "Setting %s to '%s' at level %s mode append %s" % # [name.inspect,value,self.level, append] if table.include?(name) unless options[:append] error = Puppet::ParseError.new("Cannot reassign variable %s" % name) else error = Puppet::ParseError.new("Cannot append, variable %s is defined in this scope" % name) end if options[:file] error.file = options[:file] end if options[:line] error.line = options[:line] end raise error end unless options[:append] table[name] = value else # append case # lookup the value in the scope if it exists and insert the var table[name] = lookupvar(name) # concatenate if string, append if array, nothing for other types case value when Array table[name] += value when Hash raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" unless value.is_a?(Hash) table[name].merge!(value) else table[name] << value end end end # Return an interpolated string. def strinterp(string, file = nil, line = nil) # Most strings won't have variables in them. ss = StringScanner.new(string) out = "" while not ss.eos? if ss.scan(/^\$\{((\w*::)*\w+|[0-9]+)\}|^\$([0-9])|^\$((\w*::)*\w+)/) # If it matches the backslash, then just retun the dollar sign. if ss.matched == '\\$' out << '$' else # look the variable up # make sure $0-$9 are lookupable only if ephemeral var = ss[1] || ss[3] || ss[4] if var and var =~ /^[0-9]+$/ and not ephemeral?(var) next end out << lookupvar(var).to_s || "" end elsif ss.scan(/^\\(.)/) # Puppet.debug("Got escape: pos:%d; m:%s" % [ss.pos, ss.matched]) case ss[1] when 'n' out << "\n" when 't' out << "\t" when 's' out << " " when '\\' out << '\\' when '$' out << '$' else str = "Unrecognised escape sequence '#{ss.matched}'" if file str += " in file %s" % file end if line str += " at line %s" % line end Puppet.warning str out << ss.matched end elsif ss.scan(/^\$/) out << '$' elsif ss.scan(/^\\\n/) # an escaped carriage return next else tmp = ss.scan(/[^\\$]+/) # Puppet.debug("Got other: pos:%d; m:%s" % [ss.pos, tmp]) unless tmp error = Puppet::ParseError.new("Could not parse string %s" % string.inspect) {:file= => file, :line= => line}.each do |m,v| error.send(m, v) if v end raise error end out << tmp end end return out end # 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 resource.tags end # Used mainly for logging def to_s "Scope(%s)" % @resource.to_s end # Undefine a variable; only used for testing. def unsetvar(var) table = ephemeral?(var) ? @ephemeral : @symtable if table.include?(var) table.delete(var) end end def unset_ephemeral_var @ephemeral = {} end def ephemeral?(name) @ephemeral.include?(name) end def ephemeral_from(match, file = nil, line = nil) raise(ArgumentError,"Invalid regex match data") unless match.is_a?(MatchData) setvar("0", match[0], :file => file, :line => line, :ephemeral => true) match.captures.each_with_index do |m,i| setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true) end end end diff --git a/lib/puppet/parser/resource_type.rb b/lib/puppet/resource/type.rb similarity index 99% rename from lib/puppet/parser/resource_type.rb rename to lib/puppet/resource/type.rb index c0d7f69ed..1a18b4e97 100644 --- a/lib/puppet/parser/resource_type.rb +++ b/lib/puppet/resource/type.rb @@ -1,237 +1,237 @@ require 'puppet/parser/parser' require 'puppet/util/warnings' require 'puppet/util/errors' require 'puppet/util/inline_docs' require 'puppet/parser/ast/leaf' -class Puppet::Parser::ResourceType +class Puppet::Resource::Type include Puppet::Util::InlineDocs include Puppet::Util::Warnings include Puppet::Util::Errors RESOURCE_SUPERTYPES = [:hostclass, :node, :definition] attr_accessor :file, :line, :doc, :code, :parent, :code_collection attr_reader :type, :namespace, :arguments, :behaves_like # 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 parent return true if klass == parent_type return parent_type.child_of?(klass) end # Now evaluate the code associated with this class or definition. def evaluate_code(resource) # Create a new scope. scope = subscope(resource.scope, resource) scope.compiler.class_set(name, scope) set_resource_parameters(resource, scope) return nil unless c = self.code return c.safeevaluate(scope) end def initialize(type, name, options = {}) @type = type.to_s.downcase.to_sym raise ArgumentError, "Invalid resource supertype '#{type}'" unless RESOURCE_SUPERTYPES.include?(@type) name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName) set_name_and_namespace(name) [:code, :doc, :line, :file, :parent].each do |param| next unless value = options[param] send(param.to_s + "=", value) end set_arguments(options[:arguments]) end # This is only used for node names, and really only when the node name # is a regexp. def match(string) return string.to_s.downcase == name unless name_is_regex? return @name =~ string end # Add code from a new instance to our code. def merge(other) fail ArgumentError, "#{name} is not a class; cannot add code to it" unless type == :hostclass fail ArgumentError, "#{other.name} is not a class; cannot add code from it" unless other.type == :hostclass if parent and other.parent and parent != other.parent fail ArgumentError, "Cannot merge classes with different parent classes" end # We know they're either equal or only one is set, so keep whichever parent is specified. self.parent ||= other.parent if other.doc self.doc ||= "" self.doc += other.doc end # This might just be an empty, stub class. return unless other.code unless self.code self.code = other.code return end array_class = Puppet::Parser::AST::ASTArray unless self.code.is_a?(array_class) self.code = array_class.new(:children => [self.code]) end if other.code.is_a?(array_class) code.children += other.code.children else code.children << other.code end end # Make an instance of our resource type. This is only possible # for those classes and nodes that don't have any arguments, and is # only useful for things like the 'include' function. def mk_plain_resource(scope) type == :definition and raise ArgumentError, "Cannot create resources for defined resource types" resource_type = type == :hostclass ? :class : :node # Make sure our parent class has been evaluated, if we have one. if parent and ! scope.catalog.resource(resource_type, parent) parent_type.mk_plain_resource(scope) end # Do nothing if the resource already exists; this makes sure we don't # get multiple copies of the class resource, which helps provide the # singleton nature of classes. if resource = scope.catalog.resource(resource_type, name) return resource end resource = Puppet::Parser::Resource.new(:type => resource_type, :title => name, :scope => scope, :source => self) scope.compiler.add_resource(scope, resource) scope.catalog.tag(*resource.tags) resource end def name return @name unless @name.is_a?(Regexp) return @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'') end def name_is_regex? @name.is_a?(Regexp) end def parent_type return nil unless parent unless @parent_type ||= code_collection.send(type, parent) fail Puppet::ParseError, "Could not find parent resource type '#{parent}'" end @parent_type end # Set any arguments passed by the resource as variables in the scope. def set_resource_parameters(resource, scope) set = {} resource.to_hash.each do |param, value| param = param.to_sym fail Puppet::ParseError, "#{resource.ref} does not accept attribute #{param}" unless validattr?(param) exceptwrap { scope.setvar(param.to_s, value) } set[param] = true end # Verify that all required arguments are either present or # have been provided with defaults. arguments.each do |param, default| param = param.to_sym next if set.include?(param) # Even if 'default' is a false value, it's an AST value, so this works fine fail Puppet::ParseError, "Must pass #{param} to #{resource.ref}" unless default scope.setvar(param.to_s, default.safeevaluate(scope)) end scope.setvar("title", resource.title) unless set.include? :title scope.setvar("name", resource.name) unless set.include? :name scope.class_set(self.name,scope) end # Create a new subscope in which to evaluate our code. def subscope(scope, resource) scope.newscope :resource => resource, :namespace => self.namespace, :source => self end # Check whether a given argument is valid. def validattr?(param) param = param.to_s return true if param == "name" return true if Puppet::Type.metaparam?(param) return false unless defined?(@arguments) return true if arguments.include?(param) return false end def set_arguments(arguments) @arguments = {} return if arguments.nil? arguments.each do |arg, default| arg = arg.to_s warn_if_metaparam(arg, default) @arguments[arg] = default end end private def convert_from_ast(name) value = name.value if value.is_a?(Puppet::Parser::AST::Regex) name = value.value else name = value end 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 def set_name_and_namespace(name) if name.is_a?(Regexp) @name = name @namespace = "" else @name = name.to_s.downcase @namespace, ignored_shortname = namesplit(@name) end end def warn_if_metaparam(param, default) return unless Puppet::Type.metaparamclass(param) if default warnonce "#{param} is a metaparam; this value will inherit to all contained resources" else raise Puppet::ParseError, "#{param} is a metaparameter; please choose another parameter name in the #{self.name} definition" end end end diff --git a/lib/puppet/parser/resource_type_collection.rb b/lib/puppet/resource/type_collection.rb similarity index 99% rename from lib/puppet/parser/resource_type_collection.rb rename to lib/puppet/resource/type_collection.rb index cb90d9bb9..7ca95b1b8 100644 --- a/lib/puppet/parser/resource_type_collection.rb +++ b/lib/puppet/resource/type_collection.rb @@ -1,182 +1,182 @@ -class Puppet::Parser::ResourceTypeCollection +class Puppet::Resource::TypeCollection attr_reader :environment def initialize(env) @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env @hostclasses = {} @definitions = {} @nodes = {} # So we can keep a list and match the first-defined regex @node_list = [] @watched_files = {} end def <<(thing) add(thing) self end def add(instance) method = "add_#{instance.type}" send(method, instance) instance.code_collection = self instance end def add_hostclass(instance) dupe_check(instance, @hostclasses) { |dupe| "Class #{instance.name} is already defined#{dupe.error_context}; cannot redefine" } dupe_check(instance, @definitions) { |dupe| "Definition #{instance.name} is already defined#{dupe.error_context}; cannot be redefined as a class" } @hostclasses[instance.name] = instance instance end def hostclass(name) @hostclasses[munge_name(name)] end def add_node(instance) dupe_check(instance, @nodes) { |dupe| "Node #{instance.name} is already defined#{dupe.error_context}; cannot redefine" } @node_list << instance @nodes[instance.name] = instance instance end def node(name) name = munge_name(name) if node = @nodes[name] return node end @node_list.each do |node| next unless node.name_is_regex? return node if node.match(name) end nil end def node_exists?(name) @nodes[munge_name(name)] end def nodes? @nodes.length > 0 end def add_definition(code) @definitions[code.name] = code end def definition(name) @definitions[munge_name(name)] end def find(namespace, name, type) if r = find_fully_qualified(name, type) return r end ary = namespace.split("::") while ary.length > 0 tmp_namespace = ary.join("::") if r = find_partially_qualified(tmp_namespace, name, type) return r end # Delete the second to last object, which reduces our namespace by one. ary.pop end send(type, name) end def find_node(name) find("", name, :node) end def find_hostclass(namespace, name) find(namespace, name, :hostclass) end def find_definition(namespace, name) find(namespace, name, :definition) end [:hostclasses, :nodes, :definitions].each do |m| define_method(m) do instance_variable_get("@#{m}").dup end end def perform_initial_import parser = Puppet::Parser::Parser.new(environment) if code = Puppet.settings.uninterpolated_value(:code, environment.to_s) and code != "" parser.string = code else file = Puppet.settings.value(:manifest, environment.to_s) return unless File.exist?(file) parser.file = file end parser.parse rescue => detail msg = "Could not parse for environment #{environment}: #{detail}" error = Puppet::Error.new(msg) error.set_backtrace(detail.backtrace) raise error end def stale? @watched_files.values.detect { |file| file.changed? } end def version return @version if defined?(@version) if environment[:config_version] == "" @version = Time.now.to_i return @version end @version = Puppet::Util.execute([environment[:config_version]]).strip rescue Puppet::ExecutionFailure => e raise Puppet::ParseError, "Unable to set config_version: #{e.message}" end def watch_file(file) @watched_files[file] = Puppet::Util::LoadedFile.new(file) end def watching_file?(file) @watched_files.include?(file) end private def find_fully_qualified(name, type) return nil unless name =~ /^::/ name = name.sub(/^::/, '') send(type, name) end def find_partially_qualified(namespace, name, type) send(type, [namespace, name].join("::")) end def munge_name(name) name.to_s.downcase end def dupe_check(instance, hash) return unless dupe = hash[instance.name] message = yield dupe instance.fail Puppet::ParseError, message end end diff --git a/lib/puppet/resource/type_collection_helper.rb b/lib/puppet/resource/type_collection_helper.rb new file mode 100644 index 000000000..819cfba0b --- /dev/null +++ b/lib/puppet/resource/type_collection_helper.rb @@ -0,0 +1,7 @@ +require 'puppet/resource/type_collection' + +module Puppet::Resource::TypeCollectionHelper + def known_resource_types + environment.known_resource_types + end +end diff --git a/spec/integration/parser/functions/include.rb b/spec/integration/parser/functions/include.rb index 64346bffb..f84d43276 100755 --- a/spec/integration/parser/functions/include.rb +++ b/spec/integration/parser/functions/include.rb @@ -1,25 +1,25 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe "The include function" do before :each do @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.send(:evaluate_main) @scope = @compiler.topscope # preload our functions Puppet::Parser::Functions.function(:include) Puppet::Parser::Functions.function(:require) end it "should add a containment relationship between the 'included' class and our class" do - @compiler.known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, "includedclass") + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "includedclass") @scope.function_include("includedclass") klass_resource = @compiler.findresource(:class,"includedclass") klass_resource.should be_instance_of(Puppet::Parser::Resource) @compiler.catalog.should be_edge(@scope.resource, klass_resource) end end diff --git a/spec/integration/parser/functions/require.rb b/spec/integration/parser/functions/require.rb index 5f95ec520..143729967 100755 --- a/spec/integration/parser/functions/require.rb +++ b/spec/integration/parser/functions/require.rb @@ -1,44 +1,44 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe "The require function" do before :each do @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.send(:evaluate_main) @compiler.catalog.client_version = "0.25" @scope = @compiler.topscope # preload our functions Puppet::Parser::Functions.function(:include) Puppet::Parser::Functions.function(:require) end it "should add a dependency between the 'required' class and our class" do - @compiler.known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, "requiredclass") + @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass") @scope.function_require("requiredclass") @scope.resource["require"].should_not be_nil ref = @scope.resource["require"].shift ref.type.should == "Class" ref.title.should == "requiredclass" end it "should queue relationships between the 'required' class and our classes" do @parser.newclass("requiredclass1") @parser.newclass("requiredclass2") @scope.function_require("requiredclass1") @scope.function_require("requiredclass2") @scope.resource["require"].should_not be_nil (ref1,ref2) = @scope.resource["require"] ref1.type.should == "Class" ref1.title.should == "requiredclass1" ref2.type.should == "Class" ref2.title.should == "requiredclass2" end end diff --git a/spec/integration/parser/parser.rb b/spec/integration/parser/parser.rb index 08cab5621..5a30f066e 100755 --- a/spec/integration/parser/parser.rb +++ b/spec/integration/parser/parser.rb @@ -1,21 +1,21 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Parser::Parser do before :each do - @resource_type_collection = Puppet::Parser::ResourceTypeCollection.new("env") + @resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser = Puppet::Parser::Parser.new "development" end describe "when parsing comments before statement" do it "should associate the documentation to the statement AST node" do ast = @parser.parse(""" # comment class test {} """) ast.hostclass("test").doc.should == "comment\n" end end end diff --git a/spec/integration/util/rdoc/parser.rb b/spec/integration/util/rdoc/parser.rb index a52c11d87..01bcb644b 100755 --- a/spec/integration/util/rdoc/parser.rb +++ b/spec/integration/util/rdoc/parser.rb @@ -1,59 +1,59 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } -require 'puppet/parser/resource_type_collection' +require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' describe RDoc::Parser do require 'puppet_spec/files' include PuppetSpec::Files before :each do tmpdir = tmpfile('rdoc_parser_tmp') Dir.mkdir(tmpdir) @parsedfile = File.join(tmpdir, 'init.pp') File.open(@parsedfile, 'w') do |f| f.puts '# comment' f.puts 'class ::test {}' end @top_level = stub_everything 'toplevel', :file_relative_name => @parsedfile @module = stub_everything 'module' @puppet_top_level = RDoc::PuppetTopLevel.new(@top_level) RDoc::PuppetTopLevel.stubs(:new).returns(@puppet_top_level) @puppet_top_level.expects(:add_module).returns(@module) @parser = RDoc::Parser.new(@top_level, @parsedfile, nil, Options.instance, RDoc::Stats.new) end after(:each) do File.unlink(@parsedfile) end def get_test_class(toplevel) # toplevel -> main -> test toplevel.classes[0].classes[0] end it "should parse to RDoc data structure" do - @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Parser::ResourceType) } + @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Resource::Type) } @parser.scan end it "should get a PuppetClass for the main class" do @parser.scan.classes[0].should be_a(RDoc::PuppetClass) end it "should produce a PuppetClass whose name is test" do get_test_class(@parser.scan).name.should == "test" end it "should produce a PuppetClass whose comment is 'comment'" do get_test_class(@parser.scan).comment.should == "comment\n" end end diff --git a/spec/unit/application/main.rb b/spec/unit/application/main.rb index ca185cb0a..74c40c14a 100755 --- a/spec/unit/application/main.rb +++ b/spec/unit/application/main.rb @@ -1,384 +1,384 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/main' describe "Puppet" do before :each do @main = Puppet::Application[:main] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| it "should declare handle_#{option} method" do @main.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @main.options.expects(:[]=).with(option, 'arg') @main.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @main.options.expects(:[]=).with(:code, 'arg') @main.send("handle_execute".to_sym, 'arg') end it "should ask Puppet::Application to parse Puppet configuration file" do @main.should_parse_config?.should be_true end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @main.handle_logdest("console") end it "should put the logset options to true" do @main.options.expects(:[]=).with(:logset,true) @main.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:trap) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::Network::Client.dipper.stubs(:new) STDIN.stubs(:read) @main.options.stubs(:[]).with(any_parameters) end it "should set show_diff on --noop" do Puppet.stubs(:[]=) Puppet.stubs(:[]).with(:config) Puppet.stubs(:[]).with(:noop).returns(true) Puppet.expects(:[]=).with(:show_diff, true) @main.run_setup end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @main.run_setup end it "should set INT trap" do @main.expects(:trap).with(:INT) @main.run_setup end it "should set log level to debug if --debug was passed" do @main.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @main.run_setup end it "should set log level to info if --verbose was passed" do @main.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @main.run_setup end it "should print puppet config if asked to in Puppet config" do @main.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @main.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @main.run_setup }.should raise_error(SystemExit) end end describe "when executing" do it "should dispatch to parseonly if parseonly is set" do @main.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(true) @main.get_command.should == :parseonly end it "should dispatch to 'apply' if it was called with 'apply'" do @main.options[:catalog] = "foo" @main.get_command.should == :apply end it "should dispatch to main if parseonly is not set" do @main.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(false) @main.get_command.should == :main end describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @main.stubs(:exit) @main.options.stubs(:[]).with(:code).returns "some code" @collection = stub_everything - Puppet::Parser::ResourceTypeCollection.stubs(:new).returns(@collection) + Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) end it "should use a Puppet Resource Type Collection to parse the file" do @collection.expects(:perform_initial_import) @main.parseonly end it "should exit with exit code 0 if no error" do @main.expects(:exit).with(0) @main.parseonly end it "should exit with exit code 1 if error" do @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @main.expects(:exit).with(1) @main.parseonly end end describe "the main command" do before :each do Puppet.stubs(:[]) Puppet.settings.stubs(:use) Puppet.stubs(:[]).with(:prerun_command).returns "" Puppet.stubs(:[]).with(:postrun_command).returns "" Puppet.stubs(:[]).with(:trace).returns(true) @main.options.stubs(:[]) @facts = stub_everything 'facts' Puppet::Node::Facts.stubs(:find).returns(@facts) @node = stub_everything 'node' Puppet::Node.stubs(:find).returns(@node) @catalog = stub_everything 'catalog' @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub_everything 'transaction' @catalog.stubs(:apply).returns(@transaction) @main.stubs(:exit) end it "should set the code to run from --code" do @main.options.stubs(:[]).with(:code).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @main.main end it "should set the code to run from STDIN if no arguments" do ARGV.stubs(:length).returns(0) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @main.main end it "should set the manifest if some files are passed on command line" do ARGV.stubs(:length).returns(1) ARGV.stubs(:shift).returns("site.pp") Puppet.expects(:[]=).with(:manifest,"site.pp") @main.main end it "should collect the node facts" do Puppet::Node::Facts.expects(:find).returns(@facts) @main.main end it "should raise an error if we can't find the node" do Puppet::Node::Facts.expects(:find).returns(nil) lambda { @main.main }.should raise_error end it "should find the node" do Puppet::Node.expects(:find).returns(@node) @main.main end it "should raise an error if we can't find the node" do Puppet::Node.expects(:find).returns(nil) lambda { @main.main }.should raise_error end it "should merge in our node the loaded facts" do @facts.stubs(:values).returns("values") @node.expects(:merge).with("values") @main.main end it "should load custom classes if loadclasses" do @main.options.stubs(:[]).with(:loadclasses).returns(true) Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") @node.expects(:classes=) @main.main end it "should compile the catalog" do Puppet::Resource::Catalog.expects(:find).returns(@catalog) @main.main end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) @main.main end it "should finalize the catalog" do @catalog.expects(:finalize) @main.main end it "should call the prerun and postrun commands on a Configurer instance" do configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:execute_prerun_command) configurer.expects(:execute_postrun_command) @main.main end it "should apply the catalog" do @catalog.expects(:apply) @main.main end describe "with detailed_exitcodes" do it "should exit with report's computed exit status" do Puppet.stubs(:[]).with(:noop).returns(false) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(666) @main.main end it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(false) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(0) @main.main end it "should always exit with 0 if --noop" do Puppet.stubs(:[]).with(:noop).returns(true) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(0) @main.main end end end describe "the 'apply' command" do it "should read the catalog in from disk if a file name is provided" do @main.options[:catalog] = "/my/catalog.pson" File.expects(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should read the catalog in from stdin if '-' is provided" do @main.options[:catalog] = "-" $stdin.expects(:read).returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should deserialize the catalog from the default format" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should fail helpfully if deserializing fails" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" lambda { @main.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @main.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run).with(:catalog => "mycatalog") @main.apply end end end end diff --git a/spec/unit/application/server.rb b/spec/unit/application/server.rb index ba0fcfef2..b300e941e 100644 --- a/spec/unit/application/server.rb +++ b/spec/unit/application/server.rb @@ -1,450 +1,450 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/server' describe "PuppetMaster" do before :each do @server_app = Puppet::Application[:server] @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Node.stubs(:terminus_class=) Puppet::Node.stubs(:cache_class=) Puppet::Node::Facts.stubs(:terminus_class=) Puppet::Node::Facts.stubs(:cache_class=) Puppet::Transaction::Report.stubs(:terminus_class=) Puppet::Resource::Catalog.stubs(:terminus_class=) end it "should ask Puppet::Application to parse Puppet configuration file" do @server_app.should_parse_config?.should be_true end it "should declare a main command" do @server_app.should respond_to(:main) end it "should declare a parseonly command" do @server_app.should respond_to(:parseonly) end it "should declare a compile command" do @server_app.should respond_to(:compile) end it "should declare a preinit block" do @server_app.should respond_to(:run_preinit) end describe "during preinit" do before :each do @server_app.stubs(:trap) end it "should catch INT" do @server_app.stubs(:trap).with { |arg,block| arg == :INT } @server_app.run_preinit end it "should create a Puppet Daemon" do Puppet::Daemon.expects(:new).returns(@daemon) @server_app.run_preinit end it "should give ARGV to the Daemon" do argv = stub 'argv' ARGV.stubs(:dup).returns(argv) @daemon.expects(:argv=).with(argv) @server_app.run_preinit end end [:debug,:verbose].each do |option| it "should declare handle_#{option} method" do @server_app.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @server_app.options.expects(:[]=).with(option, 'arg') @server_app.send("handle_#{option}".to_sym, 'arg') end end describe "when applying options" do before do @old_argv = ARGV.dup ARGV.clear end after do ARGV.clear @old_argv.each { |a| ARGV << a } end it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @server_app.handle_logdest("console") end it "should put the setdest options to true" do @server_app.options.expects(:[]=).with(:setdest,true) @server_app.handle_logdest("console") end it "should parse the log destination from ARGV" do ARGV << "--logdest" << "/my/file" Puppet::Util::Log.expects(:newdestination).with("/my/file") @server_app.parse_options end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet::SSL::CertificateAuthority.stubs(:instance) Puppet::SSL::CertificateAuthority.stubs(:ca?) Puppet.settings.stubs(:use) @server_app.options.stubs(:[]).with(any_parameters) end it "should set log level to debug if --debug was passed" do @server_app.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @server_app.run_setup end it "should set log level to info if --verbose was passed" do @server_app.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @server_app.run_setup end it "should set console as the log destination if no --logdest and --daemonize" do @server_app.stubs(:[]).with(:daemonize).returns(:false) Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should set syslog as the log destination if no --logdest and not --daemonize" do Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should set syslog as the log destination if --rack" do @server_app.options.stubs(:[]).with(:rack).returns(:true) Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should print puppet config if asked to in Puppet config" do @server_app.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @server_app.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @server_app.run_setup }.should raise_error(SystemExit) end it "should tell Puppet.settings to use :main,:ssl and :server_app category" do Puppet.settings.expects(:use).with(:main,:puppetmasterd,:ssl) @server_app.run_setup end it "should set node facst terminus to yaml" do Puppet::Node::Facts.expects(:terminus_class=).with(:yaml) @server_app.run_setup end it "should cache class in yaml" do Puppet::Node.expects(:cache_class=).with(:yaml) @server_app.run_setup end describe "with no ca" do it "should set the ca_location to none" do Puppet::SSL::Host.expects(:ca_location=).with(:none) @server_app.run_setup end end describe "with a ca configured" do before :each do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) end it "should set the ca_location to local" do Puppet::SSL::Host.expects(:ca_location=).with(:local) @server_app.run_setup end it "should tell Puppet.settings to use :ca category" do Puppet.settings.expects(:use).with(:ca) @server_app.run_setup end it "should instantiate the CertificateAuthority singleton" do Puppet::SSL::CertificateAuthority.expects(:instance) @server_app.run_setup end end end describe "when running" do it "should dispatch to parseonly if parseonly is set" do Puppet.stubs(:[]).with(:parseonly).returns(true) @server_app.options[:node] = nil @server_app.get_command.should == :parseonly end it "should dispatch to compile if called with --compile" do @server_app.options[:node] = "foo" @server_app.get_command.should == :compile end it "should dispatch to main if parseonly is not set" do Puppet.stubs(:[]).with(:parseonly).returns(false) @server_app.options[:node] = nil @server_app.get_command.should == :main end describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @server_app.stubs(:exit) @collection = stub_everything - Puppet::Parser::ResourceTypeCollection.stubs(:new).returns(@collection) + Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) end it "should use a Puppet Resource Type Collection to parse the file" do @collection.expects(:perform_initial_import) @server_app.parseonly end it "should exit with exit code 0 if no error" do @server_app.expects(:exit).with(0) @server_app.parseonly end it "should exit with exit code 1 if error" do @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @server_app.expects(:exit).with(1) @server_app.parseonly end end describe "the compile command" do before do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @server_app.stubs(:exit) Puppet.features.stubs(:pson?).returns true end it "should fail if pson isn't available" do Puppet.features.expects(:pson?).returns false lambda { @server_app.compile }.should raise_error end it "should compile a catalog for the specified node" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).with("foo").returns Puppet::Resource::Catalog.new $stdout.stubs(:puts) @server_app.compile end it "should render the catalog to pson and print the output" do @server_app.options[:node] = "foo" catalog = Puppet::Resource::Catalog.new catalog.expects(:render).with(:pson).returns "mypson" Puppet::Resource::Catalog.expects(:find).returns catalog $stdout.expects(:puts).with("mypson") @server_app.compile end it "should exit with error code 30 if no catalog can be found" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).returns nil @server_app.expects(:exit).with(30) $stderr.expects(:puts) @server_app.compile end it "should exit with error code 30 if there's a failure" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).raises ArgumentError @server_app.expects(:exit).with(30) $stderr.expects(:puts) @server_app.compile end end describe "the main command" do before :each do @server_app.run_preinit @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @app = stub_everything 'app' Puppet::SSL::Host.stubs(:localhost) Puppet::SSL::CertificateAuthority.stubs(:ca?) Process.stubs(:uid).returns(1000) Puppet.stubs(:service) Puppet.stubs(:[]) Puppet.stubs(:notice) Puppet.stubs(:start) end it "should create a Server" do Puppet::Network::Server.expects(:new) @server_app.main end it "should give the server to the daemon" do @daemon.expects(:server=).with(@server) @server_app.main end it "should create the server with the right XMLRPC handlers" do Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} @server_app.main end it "should create the server with a :ca xmlrpc handler if needed" do Puppet.stubs(:[]).with(:ca).returns(true) Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } @server_app.main end it "should generate a SSL cert for localhost" do Puppet::SSL::Host.expects(:localhost) @server_app.main end it "should make sure to *only* hit the CA for data" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:only) @server_app.main end it "should drop privileges if running as root" do Process.stubs(:uid).returns(0) Puppet::Util.expects(:chuser) @server_app.main end it "should daemonize if needed" do Puppet.stubs(:[]).with(:daemonize).returns(true) @daemon.expects(:daemonize) @server_app.main end it "should start the service" do @daemon.expects(:start) @server_app.main end describe "with --rack" do confine "Rack is not available" => Puppet.features.rack? before do require 'puppet/network/http/rack' Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) end it "it should create the app with REST and XMLRPC support" do @server_app.options.stubs(:[]).with(:rack).returns(:true) Puppet::Network::HTTP::Rack.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and args[:protocols] == [:rest, :xmlrpc] } @server_app.main end it "it should not start a daemon" do @server_app.options.stubs(:[]).with(:rack).returns(:true) @daemon.expects(:start).never @server_app.main end it "it should return the app" do @server_app.options.stubs(:[]).with(:rack).returns(:true) app = @server_app.main app.should equal(@app) end end end end end diff --git a/spec/unit/node/environment.rb b/spec/unit/node/environment.rb index 26d9aabe1..81981973d 100755 --- a/spec/unit/node/environment.rb +++ b/spec/unit/node/environment.rb @@ -1,219 +1,219 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/node/environment' require 'puppet/util/execution' describe Puppet::Node::Environment do after do Puppet::Node::Environment.clear end it "should include the Cacher module" do Puppet::Node::Environment.ancestors.should be_include(Puppet::Util::Cacher) end it "should use the filetimeout for the ttl for the modulepath" do Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the manifestdir" do Puppet::Node::Environment.attr_ttl(:manifestdir).should == Integer(Puppet[:filetimeout]) end it "should use the default environment if no name is provided while initializing an environment" do Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new().name.should == :one end it "should treat environment instances as singletons" do Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end it "should return its name when converted to a string" do Puppet::Node::Environment.new(:one).to_s.should == "one" end describe "when managing known resource types" do before do @env = Puppet::Node::Environment.new("dev") - @collection = Puppet::Parser::ResourceTypeCollection.new(@env) + @collection = Puppet::Resource::TypeCollection.new(@env) @collection.stubs(:perform_initial_import) end it "should create a resource type collection if none exists" do - Puppet::Parser::ResourceTypeCollection.expects(:new).with(@env).returns @collection + Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection @env.known_resource_types.should equal(@collection) end it "should reuse any existing resource type collection" do @env.known_resource_types.should equal(@env.known_resource_types) end it "should perform the initial import when creating a new collection" do @collection.expects(:perform_initial_import) - Puppet::Parser::ResourceTypeCollection.expects(:new).returns @collection + Puppet::Resource::TypeCollection.expects(:new).returns @collection @env.known_resource_types end it "should create and return a new collection rather than returning a stale collection" do @env.known_resource_types.expects(:stale?).returns true - Puppet::Parser::ResourceTypeCollection.expects(:new).returns @collection + Puppet::Resource::TypeCollection.expects(:new).returns @collection @env.known_resource_types.should equal(@collection) end end [:modulepath, :manifestdir].each do |setting| it "should validate the #{setting} directories" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.expects(:validate_dirs).with(%w{/one /two}) env.send(setting) end it "should return the validated dirs for #{setting}" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.stubs(:validate_dirs).returns %w{/one /two} env.send(setting).should == %w{/one /two} end end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do env = Puppet::Node::Environment.new("testing") module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path env.modulepath.should == %w{/l1 /l2 /one /two} end end describe "when validating modulepath or manifestdir directories" do it "should not return non-directories" do env = Puppet::Node::Environment.new("testing") FileTest.expects(:directory?).with("/one").returns true FileTest.expects(:directory?).with("/two").returns false env.validate_dirs(%w{/one /two}).should == %w{/one} end it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true env = Puppet::Node::Environment.new("testing") two = File.join(Dir.getwd, "two") env.validate_dirs(%w{/one two}).should == ["/one", two] end end describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end it "should provide an array-like accessor method for returning any environment-specific setting" do env = Puppet::Node::Environment.new("testing") env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") env = Puppet::Node::Environment.new("testing") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns true env.module("one").should equal(mod) end it "should return nil if asked for a module that does not exist in its path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns false env.module("one").should be_nil end it "should be able to return its modules" do Puppet::Node::Environment.new("testing").should respond_to(:modules) end describe ".modules" do it "should return a module named for every directory in each module path" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo bar} Dir.expects(:entries).with("/b").returns %w{bee baz} env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).returns( %w{/a /b} ).at_least_once Dir.expects(:entries).with("/a").returns %w{foo} Dir.expects(:entries).with("/b").returns %w{foo} env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore invalid modules" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).returns( %w{/a} ) Dir.expects(:entries).with("/a").returns %w{foo bar} Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should create modules with the correct environment" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo} env.modules.each {|mod| mod.environment.should == env } end it "should cache the module list" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules env.modules end end end end diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb index 4646f77a3..333046c77 100755 --- a/spec/unit/parser/compiler.rb +++ b/spec/unit/parser/compiler.rb @@ -1,605 +1,605 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def ref "%s[%s]" % [type.to_s.capitalize, title] end def evaluated? @evaluated end def builtin? @builtin end def virtual? @virtual end def evaluate end end describe Puppet::Parser::Compiler do before :each do @node = Puppet::Node.new "testnode" - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new "development" + @known_resource_types = Puppet::Resource::TypeCollection.new "development" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]', :type => "class" @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should have a class method that compiles, converts, and returns a catalog" do compiler = stub 'compiler' Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler catalog = stub 'catalog' compiler.expects(:compile).returns catalog converted_catalog = stub 'converted_catalog' catalog.expects(:to_resource).returns converted_catalog Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) end it "should fail intelligently when a class-level compile fails" do Puppet::Parser::Compiler.expects(:new).raises ArgumentError lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) end it "should use the node's environment as its environment" do @compiler.environment.should equal(@node.environment) end it "should include the resource type collection helper" do - Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Parser::ResourceTypeCollectionHelper) + Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should be able to return a class list containing all added classes" do @compiler.add_class "" @compiler.add_class "one" @compiler.add_class "two" @compiler.classlist.sort.should == %w{one two}.sort end describe "when initializing" do it "should set its node attribute" do @compiler.node.should equal(@node) end it "should detect when ast nodes are absent" do @compiler.ast_nodes?.should be_false end it "should detect when ast nodes are present" do @known_resource_types.expects(:nodes?).returns true @compiler.ast_nodes?.should be_true end it "should copy the known_resource_types version to the catalog" do @compiler.catalog.version.should == @known_resource_types.version end it "should copy any node classes into the class list" do node = Puppet::Node.new("mynode") node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should include("foo") compiler.classlist.should include("bar") end end describe "when managing scopes" do it "should create a top scope" do @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) end it "should be able to create new scopes" do @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) end it "should correctly set the level of newly created scopes" do @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 end it "should set the parent scope of the new scope to be the passed-in parent" do scope = mock 'scope' newscope = @compiler.newscope(scope) newscope.parent.should equal(scope) end it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do scope = mock 'scope' newscope = @compiler.newscope(nil) newscope.parent.should equal(@compiler.topscope) end end describe "when compiling" do def compile_methods [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish, :store, :extract] end # Stub all of the main compile methods except the ones we're specifically interested in. def compile_stub(*except) (compile_methods - except).each { |m| @compiler.stubs(m) } end it "should set node parameters as variables in the top scope" do params = {"a" => "b", "c" => "d"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.topscope.lookupvar("a").should == "b" @compiler.topscope.lookupvar("c").should == "d" end it "should set the client and server versions on the catalog" do params = {"clientversion" => "2", "serverversion" => "3"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.catalog.client_version.should == "2" @compiler.catalog.server_version.should == "3" end it "should evaluate any existing classes named in the node" do classes = %w{one two three four} main = stub 'main' one = stub 'one', :name => "one" three = stub 'three', :name => "three" @node.stubs(:name).returns("whatever") @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = mock 'main_class' main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(main_class) @known_resource_types.stubs(:find_hostclass).with("", "").returns(main_class) @compiler.compile end it "should create a new, empty 'main' if no main class exists" do compile_stub(:evaluate_main) @compiler.compile - @known_resource_types.find_hostclass("", "").should be_instance_of(Puppet::Parser::ResourceType) + @known_resource_types.find_hostclass("", "").should be_instance_of(Puppet::Resource::Type) end it "should evaluate any node classes" do @node.stubs(:classes).returns(%w{one two three four}) @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) @compiler.send(:evaluate_node_classes) end it "should evaluate all added collections" do colls = [] # And when the collections fail to evaluate. colls << mock("coll1-false") colls << mock("coll2-false") colls.each { |c| c.expects(:evaluate).returns(false) } @compiler.add_collection(colls[0]) @compiler.add_collection(colls[1]) compile_stub(:evaluate_generators) @compiler.compile end it "should ignore builtin resources" do resource = stub 'builtin', :ref => "File[testing]", :builtin? => true, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } @compiler.compile end it "should not evaluate already-evaluated resources" do resource = stub 'already_evaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => true, :virtual? => false, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources created by evaluating other resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) resource2 = CompilerTestResource.new(:file, "other") # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } @compiler.compile end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish" resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf = stub "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish1" resource1.expects(:finish).in_sequence(resources) @compiler.add_resource(@scope, resource1) resource2 = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish2" resource2.expects(:finish).in_sequence(resources) @compiler.add_resource(@scope, resource2) @compiler.send(:finish) end it "should return added resources in add order" do resource1 = stub "1", :ref => "File[yay]", :type => "file" @compiler.add_resource(@scope, resource1) resource2 = stub "2", :ref => "File[youpi]", :type => "file" @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = CompilerTestResource.new(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_vertex(resource) end it "should fail to add resources that conflict with existing resources" do file1 = Puppet::Type.type(:file).new :path => "/foo" file2 = Puppet::Type.type(:file).new :path => "/foo" @compiler.add_resource(@scope, file1) lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should add an edge from the scope resource to the added resource" do resource = stub "noconflict", :ref => "File[yay]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should add edges from the class resources to the main class" do main = CompilerTestResource.new(:class, :main) @compiler.add_resource(@scope, main) resource = CompilerTestResource.new(:class, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(main, resource) end it "should just add edges to the scope resource for the class resources when no main class can be found" do resource = CompilerTestResource.new(:class, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should have a method for looking up resources" do resource = stub 'resource', :ref => "Yay[foo]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.findresource("Yay[foo]").should equal(resource) end it "should be able to look resources up by type and title" do resource = stub 'resource', :ref => "Yay[foo]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => true, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end end describe "when evaluating collections" do it "should evaluate each collection" do 2.times { |i| coll = mock 'coll%s' % i @compiler.add_collection(coll) # This is the hard part -- we have to emulate the fact that # collections delete themselves if they are done evaluating. coll.expects(:evaluate).with do @compiler.delete_collection(coll) end } @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } end it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(nil) @compiler.add_collection(coll) lambda { @compiler.compile }.should_not raise_error end it "should fail when there are unevaluated resource collections that refer to a specific resource" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(:something) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns([:one, :two]) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end end describe "when told to evaluate missing classes" do it "should fail if there's no source listed for the scope" do scope = stub 'scope', :source => nil proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) end it "should tag the catalog with the name of each not-found class" do @compiler.catalog.expects(:tag).with("notfound") @scope.expects(:find_hostclass).with("notfound").returns(nil) @compiler.evaluate_classes(%w{notfound}, @scope) end end describe "when evaluating found classes" do before do @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass").returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:mk_plain_resource).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:mk_plain_resource).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should immediately evaluate the resources created for found classes when asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate) @class.expects(:mk_plain_resource).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass").returns(@class) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{MyClass}, @scope, false) end it "should return the list of found classes" do @compiler.catalog.stubs(:tag) @compiler.stubs(:add_resource) @scope.stubs(:find_hostclass).with("notfound").returns(nil) @scope.stubs(:class_scope).with(@class) Puppet::Parser::Resource.stubs(:new).returns(@resource) @class.stubs :mk_plain_resource @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} end end describe "when evaluating AST nodes with no AST nodes present" do it "should do nothing" do @compiler.expects(:ast_nodes?).returns(false) @compiler.known_resource_types.expects(:nodes).never Puppet::Parser::Resource.expects(:new).never @compiler.send(:evaluate_ast_node) end end describe "when evaluating AST nodes with AST nodes present" do before do @compiler.known_resource_types.stubs(:nodes?).returns true # Set some names for our test @node.stubs(:names).returns(%w{a b c}) @compiler.known_resource_types.stubs(:node).with("a").returns(nil) @compiler.known_resource_types.stubs(:node).with("b").returns(nil) @compiler.known_resource_types.stubs(:node).with("c").returns(nil) # It should check this last, of course. @compiler.known_resource_types.stubs(:node).with("default").returns(nil) end it "should fail if the named node cannot be found" do proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) end it "should evaluate the first node class matching the node name" do node_class = stub 'node', :name => "c", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) @compiler.compile end it "should match the default node if no matching node can be found" do node_class = stub 'node', :name => "default", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) @compiler.compile end it "should evaluate the node resource immediately rather than using lazy evaluation" do node_class = stub 'node', :name => "c" @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end it "should set the node's scope as the top scope" do node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class = stub 'node', :name => "c", :mk_plain_resource => node_resource @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) # The #evaluate method normally does this. scope = stub 'scope', :source => "mysource" @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) node_resource.stubs(:evaluate) @compiler.compile @compiler.topscope.should equal(scope) end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "My[ref]", :type => "my" @resource = stub 'resource', :ref => "My[ref]", :builtin? => true, :type => "my" end it "should be able to store overrides" do lambda { @compiler.add_override(@override) }.should_not raise_error end it "should apply overrides to the appropriate resources" do @compiler.add_resource(@scope, @resource) @resource.expects(:merge).with(@override) @compiler.add_override(@override) @compiler.compile end it "should accept overrides before the related resource has been created" do @resource.expects(:merge).with(@override) # First store the override @compiler.add_override(@override) # Then the resource @compiler.add_resource(@scope, @resource) # And compile, so they get resolved @compiler.compile end it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/parser/parser.rb b/spec/unit/parser/parser.rb index cb96e0cb7..bb0fa0a7b 100755 --- a/spec/unit/parser/parser.rb +++ b/spec/unit/parser/parser.rb @@ -1,414 +1,414 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Parser do ast = Puppet::Parser::AST before :each do - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new("development") + @known_resource_types = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @known_resource_types @true_ast = Puppet::Parser::AST::Boolean.new :value => true end it "should require an environment at initialization" do lambda { Puppet::Parser::Parser.new }.should raise_error(ArgumentError) end it "should set the environment" do env = Puppet::Node::Environment.new Puppet::Parser::Parser.new(env).environment.should == env end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") Puppet::Parser::Parser.new("testing").environment.should == env end it "should be able to look up the environment-specific resource type collection" do rtc = Puppet::Node::Environment.new("development").known_resource_types parser = Puppet::Parser::Parser.new "development" parser.known_resource_types.should equal(rtc) end describe "when parsing files" do before do FileTest.stubs(:exist?).returns true File.stubs(:open) @parser.stubs(:watch_file) end it "should treat files ending in 'rb' as ruby files" do @parser.expects(:parse_ruby_file) @parser.file = "/my/file.rb" @parser.parse end describe "in ruby" do it "should use the ruby interpreter to load the file" do @parser.file = "/my/file.rb" @parser.expects(:require).with "/my/file.rb" @parser.parse_ruby_file end end end describe "when parsing append operator" do it "should not raise syntax errors" do lambda { @parser.parse("$var += something") }.should_not raise_error end it "shouldraise syntax error on incomplete syntax " do lambda { @parser.parse("$var += ") }.should raise_error end it "should call ast::VarDef with append=true" do ast::VarDef.expects(:new).with { |h| h[:append] == true } @parser.parse("$var += 2") end it "should work with arrays too" do ast::VarDef.expects(:new).with { |h| h[:append] == true } @parser.parse("$var += ['test']") end end describe "when parsing 'if'" do it "not, it should create the correct ast objects" do ast::Not.expects(:new).with { |h| h[:value].is_a?(ast::Boolean) } @parser.parse("if ! true { $var = 1 }") end it "boolean operation, it should create the correct ast objects" do ast::BooleanOperator.expects(:new).with { |h| h[:rval].is_a?(ast::Boolean) and h[:lval].is_a?(ast::Boolean) and h[:operator]=="or" } @parser.parse("if true or true { $var = 1 }") end it "comparison operation, it should create the correct ast objects" do ast::ComparisonOperator.expects(:new).with { |h| h[:lval].is_a?(ast::Name) and h[:rval].is_a?(ast::Name) and h[:operator]=="<" } @parser.parse("if 1 < 2 { $var = 1 }") end end describe "when parsing if complex expressions" do it "should create a correct ast tree" do aststub = stub_everything 'ast' ast::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]==">" }.returns(aststub) ast::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(ast::Name) and h[:lval].is_a?(ast::Name) and h[:operator]=="==" }.returns(aststub) ast::BooleanOperator.expects(:new).with { |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" } @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") end it "should raise an error on incorrect expression" do lambda { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error end end describe "when parsing resource references" do it "should not raise syntax errors" do lambda { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error end it "should not raise syntax errors with multiple references" do lambda { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error end it "should create an ast::ResourceReference" do ast::Resource.stubs(:new) ast::ResourceReference.expects(:new).with { |arg| arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(ast::ASTArray) } @parser.parse('exec { test: command => File["a","b"] }') end end describe "when parsing resource overrides" do it "should not raise syntax errors" do lambda { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error end it "should not raise syntax errors with multiple overrides" do lambda { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error end it "should create an ast::ResourceOverride" do ast::ResourceOverride.expects(:new).with { |arg| arg[:line]==1 and arg[:object].is_a?(ast::ResourceReference) and arg[:params].is_a?(ast::ResourceParam) } @parser.parse('Resource["title1","title2"] { param => value }') end end describe "when parsing if statements" do it "should not raise errors with empty if" do lambda { @parser.parse("if true { }") }.should_not raise_error end it "should not raise errors with empty else" do lambda { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error end it "should not raise errors with empty if and else" do lambda { @parser.parse("if false { } else { }") }.should_not raise_error end it "should create a nop node for empty branch" do ast::Nop.expects(:new) @parser.parse("if true { }") end it "should create a nop node for empty else branch" do ast::Nop.expects(:new) @parser.parse("if true { notice('test') } else { }") end end describe "when parsing function calls" do it "should not raise errors with no arguments" do lambda { @parser.parse("tag()") }.should_not raise_error end it "should not raise errors with rvalue function with no args" do lambda { @parser.parse("$a = template()") }.should_not raise_error end it "should not raise errors with arguments" do lambda { @parser.parse("notice(1)") }.should_not raise_error end it "should not raise errors with multiple arguments" do lambda { @parser.parse("notice(1,2)") }.should_not raise_error end it "should not raise errors with multiple arguments and a trailing comma" do lambda { @parser.parse("notice(1,2,)") }.should_not raise_error end end describe "when parsing arrays with trailing comma" do it "should not raise errors with a trailing comma" do lambda { @parser.parse("$a = [1,2,]") }.should_not raise_error end end describe "when providing AST context" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer end it "should include the lexer's line" do @parser.ast_context[:line].should == 50 end it "should include the lexer's file" do @parser.ast_context[:file].should == "/foo/bar" end it "should include the docs if directed to do so" do @parser.ast_context(true)[:doc].should == "whev" end it "should not include the docs when told not to" do @parser.ast_context(false)[:doc].should be_nil end it "should not include the docs by default" do @parser.ast_context()[:doc].should be_nil end end describe "when building ast nodes" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer @class = stub 'class', :use_docs => false end it "should return a new instance of the provided class created with the provided options" do @class.expects(:new).with { |opts| opts[:foo] == "bar" } @parser.ast(@class, :foo => "bar") end it "should merge the ast context into the provided options" do @class.expects(:new).with { |opts| opts[:file] == "/foo" } @parser.expects(:ast_context).returns :file => "/foo" @parser.ast(@class, :foo => "bar") end it "should prefer provided options over AST context" do @class.expects(:new).with { |opts| opts[:file] == "/bar" } @parser.expects(:ast_context).returns :file => "/foo" @parser.ast(@class, :file => "/bar") end it "should include docs when the AST class uses them" do @class.expects(:use_docs).returns true @class.stubs(:new) @parser.expects(:ast_context).with(true).returns({}) @parser.ast(@class, :file => "/bar") end end describe "when creating a node" do before :each do @lexer = stub 'lexer' @lexer.stubs(:getcomment) @parser.stubs(:lexer).returns(@lexer) @node = stub_everything 'node' @parser.stubs(:ast_context).returns({}) @parser.stubs(:node).returns(nil) @nodename = stub 'nodename', :is_a? => false, :value => "foo" @nodename.stubs(:is_a?).with(Puppet::Parser::AST::HostName).returns(true) end it "should return an array of nodes" do @parser.newnode(@nodename).should be_instance_of(Array) end end describe "when retrieving a specific node" do it "should delegate to the known_resource_types node" do @known_resource_types.expects(:node).with("node") @parser.node("node") end end describe "when retrieving a specific class" do it "should delegate to the loaded code" do @known_resource_types.expects(:hostclass).with("class") @parser.hostclass("class") end end describe "when retrieving a specific definitions" do it "should delegate to the loaded code" do @known_resource_types.expects(:definition).with("define") @parser.definition("define") end end describe "when determining the configuration version" do it "should determine it from the resource type collection" do @parser.known_resource_types.expects(:version).returns "foo" @parser.version.should == "foo" end end describe "when looking up definitions" do it "should check for them by name" do @parser.stubs(:find_or_load).with("namespace","name",:definition).returns(:this_value) @parser.find_definition("namespace","name").should == :this_value end end describe "when looking up hostclasses" do it "should check for them by name" do @parser.stubs(:find_or_load).with("namespace","name",:hostclass).returns(:this_value) @parser.find_hostclass("namespace","name").should == :this_value end end describe "when looking up names" do before :each do @known_resource_types = mock 'loaded code' @known_resource_types.stubs(:find_my_type).with('loaded_namespace', 'loaded_name').returns(true) @known_resource_types.stubs(:find_my_type).with('bogus_namespace', 'bogus_name' ).returns(false) @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @known_resource_types end describe "that are already loaded" do it "should not try to load anything" do @parser.expects(:load).never @parser.find_or_load("loaded_namespace","loaded_name",:my_type) end it "should return true" do @parser.find_or_load("loaded_namespace","loaded_name",:my_type).should == true end end describe "that aren't already loaded" do it "should first attempt to load them with the all lowercase fully qualified name" do @known_resource_types.stubs(:find_my_type).with("foo_namespace","foo_name").returns(false,true,true) @parser.expects(:load).with("foo_namespace::foo_name").returns(true).then.raises(Exception) @parser.find_or_load("Foo_namespace","Foo_name",:my_type).should == true end it "should next attempt to load them with the all lowercase namespace" do @known_resource_types.stubs(:find_my_type).with("foo_namespace","foo_name").returns(false,false,true,true) @parser.expects(:load).with("foo_namespace::foo_name").returns(false).then.raises(Exception) @parser.expects(:load).with("foo_namespace" ).returns(true ).then.raises(Exception) @parser.find_or_load("Foo_namespace","Foo_name",:my_type).should == true end it "should finally attempt to load them with the all lowercase unqualified name" do @known_resource_types.stubs(:find_my_type).with("foo_namespace","foo_name").returns(false,false,false,true,true) @parser.expects(:load).with("foo_namespace::foo_name").returns(false).then.raises(Exception) @parser.expects(:load).with("foo_namespace" ).returns(false).then.raises(Exception) @parser.expects(:load).with( "foo_name").returns(true ).then.raises(Exception) @parser.find_or_load("Foo_namespace","Foo_name",:my_type).should == true end it "should return false if the name isn't found" do @parser.stubs(:load).returns(false) @parser.find_or_load("Bogus_namespace","Bogus_name",:my_type).should == false end it "should directly look for fully qualified classes" do @known_resource_types.stubs(:find_hostclass).with("foo_namespace","::foo_name").returns(false, true) @parser.expects(:load).with("foo_name").returns true @parser.find_or_load("foo_namespace","::foo_name",:hostclass) end end end describe "when loading classnames" do before :each do @known_resource_types = mock 'loaded code' @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @known_resource_types end it "should just return false if the classname is empty" do @parser.expects(:import).never @parser.load("").should == false end it "should just return true if the item is loaded" do pending "Need to access internal state (@parser's @loaded) to force this" @parser.load("").should == false end end end diff --git a/spec/unit/parser/resource.rb b/spec/unit/parser/resource.rb index eb2cd490c..bb3001c8e 100755 --- a/spec/unit/parser/resource.rb +++ b/spec/unit/parser/resource.rb @@ -1,534 +1,534 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' # LAK: FIXME This is just new tests for resources; I have # not moved all tests over yet. describe Puppet::Parser::Resource do before do @node = Puppet::Node.new("yaynode") - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new("env") + @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @source = newclass "" @scope = @compiler.topscope end def mkresource(args = {}) args[:source] ||= "source" args[:scope] ||= stub('scope', :source => mock('source')) {:type => "resource", :title => "testing", :source => "source", :scope => "scope"}.each do |param, value| args[param] ||= value end params = args[:params] || {:one => "yay", :three => "rah"} if args[:params] == :none args.delete(:params) elsif not args[:params].is_a? Array args[:params] = paramify(args[:source], params) end Puppet::Parser::Resource.new(args) end def param(name, value, source) Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) end def paramify(source, hash) hash.collect do |name, value| Puppet::Parser::Resource::Param.new( :name => name, :value => value, :source => source ) end end def newclass(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, name) + @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def newdefine(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:definition, name) + @known_resource_types.add Puppet::Resource::Type.new(:definition, name) end def newnode(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:node, name) + @known_resource_types.add Puppet::Resource::Type.new(:node, name) end it "should use the file lookup module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::FileCollection::Lookup) end it "should be isomorphic if it is builtin and models an isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(true) @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should not be isomorphic if it is builtin and models a non-isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(false) @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false end it "should be isomorphic if it is not builtin" do newdefine "whatever" @resource = Puppet::Parser::Resource.new(:type => "whatever", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should have a array-indexing method for retrieving parameter values" do @resource = mkresource @resource[:one].should == "yay" end it "should use a Puppet::Resource for converting to a ral resource" do trans = mock 'resource', :to_ral => "yay" @resource = mkresource @resource.expects(:to_resource).returns trans @resource.to_ral.should == "yay" end it "should be able to use the indexing operator to access parameters" do resource = Puppet::Parser::Resource.new(:type => "resource", :title => "testing", :source => "source", :scope => "scope") resource["foo"] = "bar" resource["foo"].should == "bar" end it "should return the title when asked for a parameter named 'title'" do Puppet::Parser::Resource.new(:type => "resource", :title => "testing", :source => "source", :scope => "scope")[:title].should == "testing" end describe "when initializing" do before do @arguments = {:type => "resource", :title => "testing", :scope => stub('scope', :source => mock('source'))} end [:type, :title, :scope].each do |name| it "should fail unless #{name.to_s} is specified" do try = @arguments.dup try.delete(name) lambda { Puppet::Parser::Resource.new(try) }.should raise_error(ArgumentError) end end it "should set the reference correctly" do res = Puppet::Parser::Resource.new(@arguments) res.ref.should == "Resource[testing]" end it "should be tagged with user tags" do tags = [ "tag1", "tag2" ] @arguments[:params] = [ param(:tag, tags , :source) ] res = Puppet::Parser::Resource.new(@arguments) (res.tags & tags).should == tags end end describe "when refering to a resource with name canonicalization" do before do @arguments = {:type => "file", :title => "/path/", :scope => stub('scope', :source => mock('source'))} end it "should canonicalize its own name" do res = Puppet::Parser::Resource.new(@arguments) res.ref.should == "File[/path]" end end describe "when evaluating" do before do @type = Puppet::Parser::Resource @definition = newdefine "mydefine" @class = newclass "myclass" @nodedef = newnode("mynode") end it "should evaluate the associated AST definition" do res = @type.new(:type => "mydefine", :title => "whatever", :scope => @scope, :source => @source) @definition.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST class" do res = @type.new(:type => "class", :title => "myclass", :scope => @scope, :source => @source) @class.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST node" do res = @type.new(:type => "node", :title => "mynode", :scope => @scope, :source => @source) @nodedef.expects(:evaluate_code).with(res) res.evaluate end end describe "when finishing" do before do @class = newclass "myclass" @nodedef = newnode("mynode") @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source) end it "should do nothing if it has already been finished" do @resource.finish @resource.expects(:add_metaparams).never @resource.finish end it "should add all defaults available from the scope" do @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) @resource.finish @resource[:owner].should == "default" end it "should not replace existing parameters with defaults" do @resource.set_parameter :owner, "oldvalue" @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) @resource.finish @resource[:owner].should == "oldvalue" end it "should add a copy of each default, rather than the actual default parameter instance" do newparam = param(:owner, "default", @resource.source) other = newparam.dup other.value = "other" newparam.expects(:dup).returns(other) @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) @resource.finish @resource[:owner].should == "other" end it "should be running in metaparam compatibility mode if running a version below 0.25" do catalog = stub 'catalog', :client_version => "0.24.8" @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should be running in metaparam compatibility mode if running no client version is available" do catalog = stub 'catalog', :client_version => nil @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should not be running in metaparam compatibility mode if running a version at or above 0.25" do catalog = stub 'catalog', :client_version => "0.25.0" @resource.stubs(:catalog).returns catalog @resource.should_not be_metaparam_compatibility_mode end it "should copy metaparams from its scope" do @scope.setvar("noop", "true") @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["noop"].should == "true" end it "should not copy metaparams that it already has" do @resource.set_parameter("noop", "false") @scope.setvar("noop", "true") @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["noop"].should == "false" end it "should not copy relationship metaparams when not in metaparam compatibility mode" do @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns false @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should be_nil end it "should copy relationship metaparams when in metaparam compatibility mode" do @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == "bar" end it "should stack relationship metaparams when in metaparam compatibility mode" do @resource.set_parameter("require", "foo") @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == ["foo", "bar"] end it "should copy all metaparams that it finds" do @scope.setvar("noop", "foo") @scope.setvar("schedule", "bar") @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["noop"].should == "foo" @resource["schedule"].should == "bar" end it "should add any tags from the scope resource" do scope_resource = stub 'scope_resource', :tags => %w{one two} @scope.stubs(:resource).returns(scope_resource) @resource.class.publicize_methods(:add_scope_tags) { @resource.add_scope_tags } @resource.tags.should be_include("one") @resource.tags.should be_include("two") end end describe "when being tagged" do before do @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} @scope = stub 'scope', :resource => @scope_resource @resource = Puppet::Parser::Resource.new(:type => "file", :title => "yay", :scope => @scope, :source => mock('source')) end it "should get tagged with the resource type" do @resource.tags.should be_include("file") end it "should get tagged with the title" do @resource.tags.should be_include("yay") end it "should get tagged with each name in the title if the title is a qualified class name" do resource = Puppet::Parser::Resource.new(:type => "file", :title => "one::two", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should get tagged with each name in the type if the type is a qualified class name" do resource = Puppet::Parser::Resource.new(:type => "one::two", :title => "whatever", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should not get tagged with non-alphanumeric titles" do resource = Puppet::Parser::Resource.new(:type => "file", :title => "this is a test", :scope => @scope, :source => mock('source')) resource.tags.should_not be_include("this is a test") end it "should fail on tags containing '*' characters" do lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) end it "should allow alpha tags" do lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) end end describe "when merging overrides" do before do @source = "source1" @resource = mkresource :source => @source @override = mkresource :source => @source end it "should fail when the override was not created by a parent class" do @override.source = "source2" @override.source.expects(:child_of?).with("source1").returns(false) lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) end it "should succeed when the override was created in the current scope" do @resource.source = "source3" @override.source = @resource.source @override.source.expects(:child_of?).with("source3").never params = {:a => :b, :c => :d} @override.expects(:params).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should succeed when a parent class created the override" do @resource.source = "source3" @override.source = "source4" @override.source.expects(:child_of?).with("source3").returns(true) params = {:a => :b, :c => :d} @override.expects(:params).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should add new parameters when the parameter is not set" do @source.stubs(:child_of?).returns true @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should replace existing parameter values" do @source.stubs(:child_of?).returns true @resource.set_parameter(:testing, "old") @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should add values to the parameter when the override was created with the '+>' syntax" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) param.add = true @override.set_parameter(param) @resource.set_parameter(:testing, "other") @resource.merge(@override) @resource[:testing].should == %w{other testing} end it "should promote tag overrides to real tags" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => "testing", :source => @resource.source) @override.set_parameter(param) @resource.merge(@override) @resource.tagged?("testing").should be_true end end it "should be able to be converted to a normal resource" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source @resource.should respond_to(:to_resource) end it "should use its resource converter to convert to a transportable resource" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source newresource = Puppet::Resource.new(:file, "/my") Puppet::Resource.expects(:new).returns(newresource) newresource.expects(:to_trans).returns "mytrans" @resource.to_trans.should == "mytrans" end it "should return nil if converted to a transportable resource and it is virtual" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source @resource.expects(:virtual?).returns true @resource.to_trans.should be_nil end describe "when being converted to a resource" do before do @source = stub 'scope', :name => "myscope" @parser_resource = mkresource :source => @source, :params => {:foo => "bar", :fee => "fum"} end it "should create an instance of Puppet::Resource" do @parser_resource.to_resource.should be_instance_of(Puppet::Resource) end it "should set the type correctly on the Puppet::Resource" do @parser_resource.to_resource.type.should == @parser_resource.type end it "should set the title correctly on the Puppet::Resource" do @parser_resource.to_resource.title.should == @parser_resource.title end it "should copy over all of the parameters" do result = @parser_resource.to_resource.to_hash # The name will be in here, also. result[:foo].should == "bar" result[:fee].should == "fum" end it "should copy over the tags" do @parser_resource.tag "foo" @parser_resource.tag "bar" @parser_resource.to_resource.tags.should == @parser_resource.tags end it "should copy over the line" do @parser_resource.line = 40 @parser_resource.to_resource.line.should == 40 end it "should copy over the file" do @parser_resource.file = "/my/file" @parser_resource.to_resource.file.should == "/my/file" end it "should copy over the 'exported' value" do @parser_resource.exported = true @parser_resource.to_resource.exported.should be_true end it "should copy over the 'virtual' value" do @parser_resource.virtual = true @parser_resource.to_resource.virtual.should be_true end it "should convert any parser resource references to Puppet::Resource::Reference instances" do ref = Puppet::Parser::Resource::Reference.new(:title => "/my/file", :type => "file") @parser_resource = mkresource :source => @source, :params => {:foo => "bar", :fee => ref} result = @parser_resource.to_resource result[:fee].should == Puppet::Resource::Reference.new(:file, "/my/file") end it "should convert any parser resource references to Puppet::Resource::Reference instances even if they are in an array" do ref = Puppet::Parser::Resource::Reference.new(:title => "/my/file", :type => "file") @parser_resource = mkresource :source => @source, :params => {:foo => "bar", :fee => ["a", ref]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource::Reference.new(:file, "/my/file")] end it "should convert any parser resource references to Puppet::Resource::Reference instances even if they are in an array of array, and even deeper" do ref1 = Puppet::Parser::Resource::Reference.new(:title => "/my/file1", :type => "file") ref2 = Puppet::Parser::Resource::Reference.new(:title => "/my/file2", :type => "file") @parser_resource = mkresource :source => @source, :params => {:foo => "bar", :fee => ["a", [ref1,ref2]]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource::Reference.new(:file, "/my/file1"), Puppet::Resource::Reference.new(:file, "/my/file2")] end it "should fail if the same param is declared twice" do lambda do @parser_resource = mkresource :source => @source, :params => [ Puppet::Parser::Resource::Param.new( :name => :foo, :value => "bar", :source => @source ), Puppet::Parser::Resource::Param.new( :name => :foo, :value => "baz", :source => @source ) ] end.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/parser/resource/reference.rb b/spec/unit/parser/resource/reference.rb index 1034dbdda..a38604226 100755 --- a/spec/unit/parser/resource/reference.rb +++ b/spec/unit/parser/resource/reference.rb @@ -1,134 +1,134 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe Puppet::Parser::Resource::Reference do before do @type = Puppet::Parser::Resource::Reference end it "should get its environment from its scope" do env = stub 'environment' scope = stub 'scope', :environment => env @type.new(:title => "foo", :type => "bar", :scope => scope).environment.should equal(env) end it "should use the resource type collection helper to find its known resource types" do - Puppet::Parser::Resource::Reference.ancestors.should include(Puppet::Parser::ResourceTypeCollectionHelper) + Puppet::Parser::Resource::Reference.ancestors.should include(Puppet::Resource::TypeCollectionHelper) end it "should use the file lookup module" do Puppet::Parser::Resource::Reference.ancestors.should be_include(Puppet::FileCollection::Lookup) end it "should require a type" do proc { @type.new(:title => "yay") }.should raise_error(Puppet::DevError) end it "should require a title" do proc { @type.new(:type => "file") }.should raise_error(Puppet::DevError) end it "should know when it refers to a builtin type" do ref = @type.new(:type => "file", :title => "/tmp/yay") ref.builtin?.should be_true ref.builtintype.should equal(Puppet::Type.type(:file)) end it "should return a downcased relationship-style resource reference for defined types" do ref = @type.new(:type => "file", :title => "/tmp/yay") ref.to_ref.should == ["file", "/tmp/yay"] end it "should return a capitalized relationship-style resource reference for defined types" do ref = @type.new(:type => "whatever", :title => "/tmp/yay") ref.to_ref.should == ["Whatever", "/tmp/yay"] end it "should return a resource reference string when asked" do ref = @type.new(:type => "file", :title => "/tmp/yay") ref.to_s.should == "File[/tmp/yay]" end it "should canonize resource reference types" do ref = @type.new(:type => "foo::bar", :title => "/tmp/yay") ref.to_s.should == "Foo::Bar[/tmp/yay]" end it "should canonize resource reference values" do ref = @type.new(:type => "file", :title => "/tmp/yay/") ref.to_s.should == "File[/tmp/yay]" end it "should canonize resource reference values without order dependencies" do args = [[:title, "/tmp/yay/"], [:type, "file"]] ref = @type.new(args) ref.to_s.should == "File[/tmp/yay]" end end describe Puppet::Parser::Resource::Reference, " when modeling defined types" do def newclass(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, name) + @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def newdefine(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:definition, name) + @known_resource_types.add Puppet::Resource::Type.new(:definition, name) end def newnode(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:node, name) + @known_resource_types.add Puppet::Resource::Type.new(:node, name) end before do @type = Puppet::Parser::Resource::Reference - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new("myenv") + @known_resource_types = Puppet::Resource::TypeCollection.new("myenv") @definition = newdefine("mydefine") @class = newclass("myclass") @nodedef = newnode("mynode") @node = Puppet::Node.new("yaynode") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should be able to find defined types" do ref = @type.new(:type => "mydefine", :title => "/tmp/yay", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.should equal(@definition) end it "should be able to find classes" do ref = @type.new(:type => "class", :title => "myclass", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.should equal(@class) end it "should be able to find nodes" do ref = @type.new(:type => "node", :title => "mynode", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.object_id.should == @nodedef.object_id end it "should only look for fully qualified classes" do top = newclass "top" sub = newclass "other::top" scope = @compiler.topscope.class.new(:parent => @compiler.topscope, :namespace => "other", :compiler => @compiler) ref = @type.new(:type => "class", :title => "top", :scope => scope) ref.definedtype.name.should equal(top.name) end it "should only look for fully qualified definitions" do top = newdefine "top" sub = newdefine "other::top" scope = @compiler.topscope.class.new(:parent => @compiler.topscope, :namespace => "other", :compiler => @compiler) ref = @type.new(:type => "top", :title => "foo", :scope => scope) ref.definedtype.name.should equal(top.name) end end diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb index b48bd1246..799d05766 100755 --- a/spec/unit/parser/scope.rb +++ b/spec/unit/parser/scope.rb @@ -1,360 +1,360 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Parser::Scope do before :each do @topscope = Puppet::Parser::Scope.new() # This is necessary so we don't try to use the compiler to discover our parent. @topscope.parent = nil @scope = Puppet::Parser::Scope.new() @scope.parent = @topscope end it "should be able to store references to class scopes" do lambda { @scope.class_set "myname", "myscope" }.should_not raise_error end it "should be able to retrieve class scopes by name" do @scope.class_set "myname", "myscope" @scope.class_scope("myname").should == "myscope" end it "should be able to retrieve class scopes by object" do klass = mock 'ast_class' klass.expects(:name).returns("myname") @scope.class_set "myname", "myscope" @scope.class_scope(klass).should == "myscope" end # #620 - Nodes and classes should conflict, else classes don't get evaluated describe "when evaluating nodes and classes with the same name (#620)" do before do @node = stub :nodescope? => true @class = stub :nodescope? => false end it "should fail if a node already exists with the same name as the class being evaluated" do @scope.class_set("one", @node) lambda { @scope.class_set("one", @class) }.should raise_error(Puppet::ParseError) end it "should fail if a class already exists with the same name as the node being evaluated" do @scope.class_set("one", @class) lambda { @scope.class_set("one", @node) }.should raise_error(Puppet::ParseError) end end it "should get its environment from its compiler" do env = stub 'environment' compiler = stub 'compiler', :environment => env scope = Puppet::Parser::Scope.new :compiler => compiler scope.environment.should equal(env) end it "should use the resource type collection helper to find its known resource types" do - Puppet::Parser::Scope.ancestors.should include(Puppet::Parser::ResourceTypeCollectionHelper) + Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) end describe "when looking up a variable" do it "should default to an empty string" do @scope.lookupvar("var").should == "" end it "should return an string when asked for a string" do @scope.lookupvar("var", true).should == "" end it "should return ':undefined' for unset variables when asked not to return a string" do @scope.lookupvar("var", false).should == :undefined end it "should be able to look up values" do @scope.setvar("var", "yep") @scope.lookupvar("var").should == "yep" end it "should be able to look up hashes" do @scope.setvar("var", {"a" => "b"}) @scope.lookupvar("var").should == {"a" => "b"} end it "should be able to look up variables in parent scopes" do @topscope.setvar("var", "parentval") @scope.lookupvar("var").should == "parentval" end it "should prefer its own values to parent values" do @topscope.setvar("var", "parentval") @scope.setvar("var", "childval") @scope.lookupvar("var").should == "childval" end describe "and the variable is qualified" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode")) @scope.compiler = @compiler @known_resource_types = @scope.known_resource_types end def newclass(name) - @known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, name) + @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def create_class_scope(name) klass = newclass(name) Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => @scope, :source => mock('source')).evaluate return @scope.class_scope(klass) end it "should be able to look up explicitly fully qualified variables from main" do other_scope = create_class_scope("") other_scope.setvar("othervar", "otherval") @scope.lookupvar("::othervar").should == "otherval" end it "should be able to look up explicitly fully qualified variables from other scopes" do other_scope = create_class_scope("other") other_scope.setvar("var", "otherval") @scope.lookupvar("::other::var").should == "otherval" end it "should be able to look up deeply qualified variables" do other_scope = create_class_scope("other::deep::klass") other_scope.setvar("var", "otherval") @scope.lookupvar("other::deep::klass::var").should == "otherval" end it "should return an empty string for qualified variables that cannot be found in other classes" do other_scope = create_class_scope("other::deep::klass") @scope.lookupvar("other::deep::klass::var").should == "" end it "should warn and return an empty string for qualified variables whose classes have not been evaluated" do klass = newclass("other::deep::klass") @scope.expects(:warning) @scope.lookupvar("other::deep::klass::var").should == "" end it "should warn and return an empty string for qualified variables whose classes do not exist" do @scope.expects(:warning) @scope.lookupvar("other::deep::klass::var").should == "" end it "should return ':undefined' when asked for a non-string qualified variable from a class that does not exist" do @scope.stubs(:warning) @scope.lookupvar("other::deep::klass::var", false).should == :undefined end it "should return ':undefined' when asked for a non-string qualified variable from a class that has not been evaluated" do @scope.stubs(:warning) klass = newclass("other::deep::klass") @scope.lookupvar("other::deep::klass::var", false).should == :undefined end end end describe "when setvar is called with append=true" do it "should raise error if the variable is already defined in this scope" do @scope.setvar("var","1", :append => false) lambda { @scope.setvar("var","1", :append => true) }.should raise_error(Puppet::ParseError) end it "should lookup current variable value" do @scope.expects(:lookupvar).with("var").returns("2") @scope.setvar("var","1", :append => true) end it "should store the concatenated string '42'" do @topscope.setvar("var","4", :append => false) @scope.setvar("var","2", :append => true) @scope.lookupvar("var").should == "42" end it "should store the concatenated array [4,2]" do @topscope.setvar("var",[4], :append => false) @scope.setvar("var",[2], :append => true) @scope.lookupvar("var").should == [4,2] end it "should store the merged hash {a => b, c => d}" do @topscope.setvar("var",{"a" => "b"}, :append => false) @scope.setvar("var",{"c" => "d"}, :append => true) @scope.lookupvar("var").should == {"a" => "b", "c" => "d"} end it "should raise an error when appending a hash with something other than another hash" do @topscope.setvar("var",{"a" => "b"}, :append => false) lambda { @scope.setvar("var","not a hash", :append => true) }.should raise_error end end describe "when calling number?" do it "should return nil if called with anything not a number" do Puppet::Parser::Scope.number?([2]).should be_nil end it "should return a Fixnum for a Fixnum" do Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) end it "should return a Float for a Float" do Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) end it "should return 234 for '234'" do Puppet::Parser::Scope.number?("234").should == 234 end it "should return nil for 'not a number'" do Puppet::Parser::Scope.number?("not a number").should be_nil end it "should return 23.4 for '23.4'" do Puppet::Parser::Scope.number?("23.4").should == 23.4 end it "should return 23.4e13 for '23.4e13'" do Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 end it "should understand negative numbers" do Puppet::Parser::Scope.number?("-234").should == -234 end it "should know how to convert exponential float numbers ala '23e13'" do Puppet::Parser::Scope.number?("23e13").should == 23e13 end it "should understand hexadecimal numbers" do Puppet::Parser::Scope.number?("0x234").should == 0x234 end it "should understand octal numbers" do Puppet::Parser::Scope.number?("0755").should == 0755 end it "should return nil on malformed integers" do Puppet::Parser::Scope.number?("0.24.5").should be_nil end it "should convert strings with leading 0 to integer if they are not octal" do Puppet::Parser::Scope.number?("0788").should == 788 end it "should convert strings of negative integers" do Puppet::Parser::Scope.number?("-0788").should == -788 end it "should return nil on malformed hexadecimal numbers" do Puppet::Parser::Scope.number?("0x89g").should be_nil end end describe "when using ephemeral variables" do it "should store the variable value" do @scope.setvar("1", :value, :ephemeral => true) @scope.lookupvar("1").should == :value end it "should remove the variable value when unset_ephemeral_var is called" do @scope.setvar("1", :value, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var @scope.lookupvar("1", false).should == :undefined end it "should not remove classic variables when unset_ephemeral_var is called" do @scope.setvar("myvar", :value1) @scope.setvar("1", :value2, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var @scope.lookupvar("myvar", false).should == :value1 end end describe "when interpolating string" do (0..9).each do |n| it "should allow $#{n} to match" do @scope.setvar(n.to_s, "value", :ephemeral => true) @scope.strinterp("$#{n}").should == "value" end end (0..9).each do |n| it "should not allow $#{n} to match if not ephemeral" do @scope.setvar(n.to_s, "value", :ephemeral => false) @scope.strinterp("$#{n}").should_not == "value" end end it "should not allow $10 to match" do @scope.setvar("10", "value", :ephemeral => true) @scope.strinterp('==$10==').should_not == "==value==" end it "should not allow ${10} to match" do @scope.setvar("10", "value", :ephemeral => true) @scope.strinterp('==${10}==').should == "==value==" end end describe "when setting ephemeral vars from matches" do before :each do @match = stub 'match', :is_a? => true @match.stubs(:[]).with(0).returns("this is a string") @match.stubs(:captures).returns([]) @scope.stubs(:setvar) end it "should accept only MatchData" do lambda { @scope.ephemeral_from("match") }.should raise_error end it "should set $0 with the full match" do @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end it "should set every capture as ephemeral var" do @match.stubs(:captures).returns([:capture1,:capture2]) @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end end describe "when unsetting variables" do it "should be able to unset normal variables" do @scope.setvar("foo", "bar") @scope.unsetvar("foo") @scope.lookupvar("foo").should == "" end it "should be able to unset ephemeral variables" do @scope.setvar("foo", "bar", :ephemeral => true) @scope.unsetvar("foo") @scope.lookupvar("foo").should == "" end end end diff --git a/spec/unit/parser/templatewrapper.rb b/spec/unit/parser/templatewrapper.rb index b1f9d2ad9..1b4121643 100755 --- a/spec/unit/parser/templatewrapper.rb +++ b/spec/unit/parser/templatewrapper.rb @@ -1,144 +1,144 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Parser::TemplateWrapper do before(:each) do - @known_resource_types = Puppet::Parser::ResourceTypeCollection.new("env") + @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @scope = Puppet::Parser::Scope.new :compiler => @compiler @file = "fake_template" Puppet::Parser::Files.stubs(:find_template).returns("/tmp/fake_template") FileTest.stubs(:exists?).returns("true") File.stubs(:read).with("/tmp/fake_template").returns("template content") @tw = Puppet::Parser::TemplateWrapper.new(@scope) end it "should create a new object TemplateWrapper from a scope" do tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper) end it "should check template file existance and read its content" do Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") File.expects(:read).with("/tmp/fake_template").returns("template content") @tw.file = @file end it "should mark the file for watching" do Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") File.stubs(:read) @known_resource_types.expects(:watch_file).with("/tmp/fake_template") @tw.file = @file end it "should fail if a template cannot be found" do Puppet::Parser::Files.expects(:find_template).returns nil lambda { @tw.file = @file }.should raise_error(Puppet::ParseError) end it "should turn into a string like template[name] for file based template" do @tw.file = @file @tw.to_s.should eql("template[/tmp/fake_template]") end it "should turn into a string like template[inline] for string-based template" do @tw.to_s.should eql("template[inline]") end it "should return the processed template contents with a call to result" do template_mock = mock("template", :result => "woot!") File.expects(:read).with("/tmp/fake_template").returns("template contents") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @tw.file = @file @tw.result.should eql("woot!") end it "should return the processed template contents with a call to result and a string" do template_mock = mock("template", :result => "woot!") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @tw.result("template contents").should eql("woot!") end it "should return the contents of a variable if called via method_missing" do @scope.expects(:lookupvar).with("chicken", false).returns("is good") tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.chicken.should eql("is good") end it "should throw an exception if a variable is called via method_missing and it does not exist" do @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) tw = Puppet::Parser::TemplateWrapper.new(@scope) lambda { tw.chicken }.should raise_error(Puppet::ParseError) end it "should allow you to check whether a variable is defined with has_variable?" do @scope.expects(:lookupvar).with("chicken", false).returns("is good") tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(true) end it "should allow you to check whether a variable is not defined with has_variable?" do @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(false) end it "should allow you to retrieve the defined classes with classes" do catalog = mock 'catalog', :classes => ["class1", "class2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.classes().should == ["class1", "class2"] end it "should allow you to retrieve all the tags with all_tags" do catalog = mock 'catalog', :tags => ["tag1", "tag2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.all_tags().should == ["tag1","tag2"] end it "should allow you to retrieve the tags defined in the current scope" do @scope.expects(:tags).returns( ["tag1", "tag2"] ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.tags().should == ["tag1","tag2"] end it "should set all of the scope's variables as instance variables" do template_mock = mock("template", :result => "woot!") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @scope.expects(:to_hash).returns("one" => "foo") @tw.result("template contents") @tw.instance_variable_get("@one").should == "foo" end it "should not error out if one of the variables is a symbol" do template_mock = mock("template", :result => "woot!") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @scope.expects(:to_hash).returns(:_timestamp => "1234") @tw.result("template contents") end %w{! . ; :}.each do |badchar| it "should translate #{badchar} to _ when setting the instance variables" do template_mock = mock("template", :result => "woot!") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @scope.expects(:to_hash).returns("one#{badchar}" => "foo") @tw.result("template contents") @tw.instance_variable_get("@one_").should == "foo" end end end diff --git a/spec/unit/parser/resource_type.rb b/spec/unit/resource/type.rb similarity index 72% rename from spec/unit/parser/resource_type.rb rename to spec/unit/resource/type.rb index 816f86176..14503b7cc 100755 --- a/spec/unit/parser/resource_type.rb +++ b/spec/unit/resource/type.rb @@ -1,533 +1,533 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/parser/resource_type' +require 'puppet/resource/type' -describe Puppet::Parser::ResourceType do +describe Puppet::Resource::Type do it "should have a 'name' attribute" do - Puppet::Parser::ResourceType.new(:hostclass, "foo").name.should == "foo" + Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" end [:code, :doc, :line, :file, :code_collection].each do |attr| it "should have a '#{attr}' attribute" do - type = Puppet::Parser::ResourceType.new(:hostclass, "foo") + type = Puppet::Resource::Type.new(:hostclass, "foo") type.send(attr.to_s + "=", "yay") type.send(attr).should == "yay" end end describe "when a node" do it "should allow a regex as its name" do - lambda { Puppet::Parser::ResourceType.new(:node, /foo/) }.should_not raise_error + lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error end it "should allow a AST::HostName instance as its name" do regex = Puppet::Parser::AST::Regex.new(:value => /foo/) name = Puppet::Parser::AST::HostName.new(:value => regex) - lambda { Puppet::Parser::ResourceType.new(:node, name) }.should_not raise_error + lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error end it "should match against the regexp in the AST::HostName when a HostName instance is provided" do regex = Puppet::Parser::AST::Regex.new(:value => /\w/) name = Puppet::Parser::AST::HostName.new(:value => regex) - node = Puppet::Parser::ResourceType.new(:node, name) + node = Puppet::Resource::Type.new(:node, name) node.match("foo").should be_true end it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do name = Puppet::Parser::AST::HostName.new(:value => "foo") - node = Puppet::Parser::ResourceType.new(:node, name) + node = Puppet::Resource::Type.new(:node, name) node.name.should == "foo" end describe "and the name is a regex" do it "should have a method that indicates that this is the case" do - Puppet::Parser::ResourceType.new(:node, /w/).should be_name_is_regex + Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex end it "should set its namespace to ''" do - Puppet::Parser::ResourceType.new(:node, /w/).namespace.should == "" + Puppet::Resource::Type.new(:node, /w/).namespace.should == "" end it "should return the regex converted to a string when asked for its name" do - Puppet::Parser::ResourceType.new(:node, /ww/).name.should == "ww" + Puppet::Resource::Type.new(:node, /ww/).name.should == "ww" end it "should downcase the regex when returning the name as a string" do - Puppet::Parser::ResourceType.new(:node, /W/).name.should == "w" + Puppet::Resource::Type.new(:node, /W/).name.should == "w" end it "should remove non-alpha characters when returning the name as a string" do - Puppet::Parser::ResourceType.new(:node, /w*w/).name.should_not include("*") + Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*") end it "should remove leading dots when returning the name as a string" do - Puppet::Parser::ResourceType.new(:node, /.ww/).name.should_not =~ /^\./ + Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./ end it "should have a method for matching its regex name against a provided name" do - Puppet::Parser::ResourceType.new(:node, /.ww/).should respond_to(:match) + Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match) end it "should return true when its regex matches the provided name" do - Puppet::Parser::ResourceType.new(:node, /\w/).match("foo").should be_true + Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true end it "should return false when its regex does not match the provided name" do - (!!Puppet::Parser::ResourceType.new(:node, /\d/).match("foo")).should be_false + (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false end it "should return true when its name, as a string, is matched against an equal string" do - Puppet::Parser::ResourceType.new(:node, "foo").match("foo").should be_true + Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true end it "should return false when its name is matched against an unequal string" do - Puppet::Parser::ResourceType.new(:node, "foo").match("bar").should be_false + Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false end it "should match names insensitive to case" do - Puppet::Parser::ResourceType.new(:node, "fOo").match("foO").should be_true + Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true end end it "should return the name converted to a string when the name is not a regex" do pending "Need to define ResourceTypeCollection behaviour first" name = Puppet::Parser::AST::HostName.new(:value => "foo") - Puppet::Parser::ResourceType.new(:node, name).name.should == "foo" + Puppet::Resource::Type.new(:node, name).name.should == "foo" end it "should return the name converted to a string when the name is a regex" do pending "Need to define ResourceTypeCollection behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) - Puppet::Parser::ResourceType.new(:node, name).name.should == /regex/.to_s + Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end it "should mark any created scopes as a node scope" do pending "Need to define ResourceTypeCollection behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) - Puppet::Parser::ResourceType.new(:node, name).name.should == /regex/.to_s + Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end end describe "when initializing" do it "should require a resource super type" do - Puppet::Parser::ResourceType.new(:hostclass, "foo").type.should == :hostclass + Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass end it "should fail if provided an invalid resource super type" do - lambda { Puppet::Parser::ResourceType.new(:nope, "foo") }.should raise_error(ArgumentError) + lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError) end it "should set its name to the downcased, stringified provided name" do - Puppet::Parser::ResourceType.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" + Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" end it "should set its namespace to the downcased, stringified qualified portion of the name" do - Puppet::Parser::ResourceType.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" + Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" end %w{code line file doc}.each do |arg| it "should set #{arg} if provided" do - type = Puppet::Parser::ResourceType.new(:hostclass, "foo", arg.to_sym => "something") + type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something") type.send(arg).should == "something" end end it "should set any provided arguments with the keys as symbols" do - type = Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) + type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) type.should be_validattr("foo") type.should be_validattr("baz") end it "should set any provided arguments with they keys as strings" do - type = Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) + type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) type.should be_validattr(:foo) type.should be_validattr(:baz) end it "should function if provided no arguments" do - type = Puppet::Parser::ResourceType.new(:hostclass, "foo") + type = Puppet::Resource::Type.new(:hostclass, "foo") type.should_not be_validattr(:foo) end end describe "when testing the validity of an attribute" do it "should return true if the parameter was typed at initialization" do - Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_validattr("foo") + Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_validattr("foo") end it "should return true if it is a metaparam" do - Puppet::Parser::ResourceType.new(:hostclass, "foo").should be_validattr("require") + Puppet::Resource::Type.new(:hostclass, "foo").should be_validattr("require") end it "should return true if the parameter is named 'name'" do - Puppet::Parser::ResourceType.new(:hostclass, "foo").should be_validattr("name") + Puppet::Resource::Type.new(:hostclass, "foo").should be_validattr("name") end it "should return false if it is not a metaparam and was not provided at initialization" do - Puppet::Parser::ResourceType.new(:hostclass, "foo").should_not be_validattr("yayness") + Puppet::Resource::Type.new(:hostclass, "foo").should_not be_validattr("yayness") end end describe "when creating a subscope" do before do @scope = stub 'scope', :newscope => nil @resource = stub 'resource' - @type = Puppet::Parser::ResourceType.new(:hostclass, "foo") + @type = Puppet::Resource::Type.new(:hostclass, "foo") end it "should return a new scope created with the provided scope as the parent" do @scope.expects(:newscope).returns "foo" @type.subscope(@scope, @resource).should == "foo" end it "should set the source as itself" do @scope.expects(:newscope).with { |args| args[:source] == @type } @type.subscope(@scope, @resource) end it "should set the scope's namespace to its namespace" do @type.expects(:namespace).returns "yayness" @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" } @type.subscope(@scope, @resource) end it "should set the scope's resource to the provided resource" do @scope.expects(:newscope).with { |args| args[:resource] == @resource } @type.subscope(@scope, @resource) end end describe "when setting its parameters in the scope" do before do @scope = stub 'scope', :newscope => nil, :setvar => nil @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]" - @type = Puppet::Parser::ResourceType.new(:hostclass, "foo") + @type = Puppet::Resource::Type.new(:hostclass, "foo") end it "should set each of the resource's parameters as variables in the scope" do @type.set_arguments :foo => nil, :boo => nil @resource.expects(:to_hash).returns(:foo => "bar", :boo => "baz") @scope.expects(:setvar).with("foo", "bar") @scope.expects(:setvar).with("boo", "baz") @scope.stubs(:class_set).with("foo",@scope) @type.set_resource_parameters(@resource, @scope) end it "should set the variables as strings" do @type.set_arguments :foo => nil @resource.expects(:to_hash).returns(:foo => "bar") @scope.expects(:setvar).with("foo", "bar") @scope.stubs(:class_set).with("foo",@scope) @type.set_resource_parameters(@resource, @scope) end it "should fail if any of the resource's parameters are not valid attributes" do @type.set_arguments :foo => nil @resource.expects(:to_hash).returns(:boo => "baz") lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should evaluate and set its default values as variables for parameters not provided by the resource" do @type.set_arguments :foo => stub("value", :safeevaluate => "something") @resource.expects(:to_hash).returns({}) @scope.expects(:setvar).with("foo", "something") @scope.stubs(:class_set).with("foo",@scope) @type.set_resource_parameters(@resource, @scope) end it "should fail if the resource does not provide a value for a required argument" do @type.set_arguments :foo => nil @resource.expects(:to_hash).returns({}) lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should set the resource's title as a variable if not otherwise provided" do @resource.expects(:to_hash).returns({}) @resource.expects(:title).returns 'teetle' @scope.expects(:setvar).with("title", "teetle") @scope.stubs(:class_set).with("foo",@scope) @type.set_resource_parameters(@resource, @scope) end it "should set the resource's name as a variable if not otherwise provided" do @resource.expects(:to_hash).returns({}) @resource.expects(:name).returns 'nombre' @scope.expects(:setvar).with("name", "nombre") @scope.stubs(:class_set).with("foo",@scope) @type.set_resource_parameters(@resource, @scope) end end describe "when describing and managing parent classes" do before do - @code = Puppet::Parser::ResourceTypeCollection.new("env") - @parent = Puppet::Parser::ResourceType.new(:hostclass, "bar") + @code = Puppet::Resource::TypeCollection.new("env") + @parent = Puppet::Resource::Type.new(:hostclass, "bar") @code.add @parent - @child = Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "bar") + @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") @code.add @child end it "should be able to define a parent" do - Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "bar") + Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") end it "should use the code collection to find the parent resource type" do @child.parent_type.should equal(@parent) end it "should be able to find parent nodes" do - parent = Puppet::Parser::ResourceType.new(:node, "bar") + parent = Puppet::Resource::Type.new(:node, "bar") @code.add parent - child = Puppet::Parser::ResourceType.new(:node, "foo", :parent => "bar") + child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") @code.add child child.parent_type.should equal(parent) end it "should cache a reference to the parent type" do @code.expects(:hostclass).once.with("bar").returns @parent @child.parent_type @child.parent_type end it "should correctly state when it is another type's child" do @child.should be_child_of(@parent) end it "should be considered the child of a parent's parent" do - @grandchild = Puppet::Parser::ResourceType.new(:hostclass, "baz", :parent => "foo") + @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") @code.add @grandchild @grandchild.should be_child_of(@parent) end it "should correctly state when it is not another type's child" do - @notchild = Puppet::Parser::ResourceType.new(:hostclass, "baz") + @notchild = Puppet::Resource::Type.new(:hostclass, "baz") @code.add @notchild @notchild.should_not be_child_of(@parent) end end describe "when evaluating its code" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new :compiler => @compiler @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]", :scope => @scope - @type = Puppet::Parser::ResourceType.new(:hostclass, "foo") + @type = Puppet::Resource::Type.new(:hostclass, "foo") @type.stubs(:set_resource_parameters) end it "should set all of its parameters in a subscope" do subscope = stub 'subscope', :compiler => @compiler @type.expects(:subscope).with(@scope, @resource).returns subscope @type.expects(:set_resource_parameters).with(@resource, subscope) @type.evaluate_code(@resource) end it "should store the class scope" do subscope = stub 'subscope', :compiler => @compiler @type.expects(:subscope).with(@scope, @resource).returns subscope @type.evaluate_code(@resource) @compiler.class_scope(@type).should equal(subscope) end it "should evaluate the code if any is provided" do code = stub 'code' @type.stubs(:code).returns code @type.stubs(:subscope).returns stub("subscope", :compiler => @compiler) code.expects(:safeevaluate).with @type.subscope @type.evaluate_code(@resource) end it "should noop if there is no code" do @type.expects(:code).returns nil @type.evaluate_code(@resource) end end describe "when creating a resource" do before do @node = Puppet::Node.new("foo") @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) - @top = Puppet::Parser::ResourceType.new :hostclass, "top" - @middle = Puppet::Parser::ResourceType.new :hostclass, "middle", :parent => "top" + @top = Puppet::Resource::Type.new :hostclass, "top" + @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" - @code = Puppet::Parser::ResourceTypeCollection.new("env") + @code = Puppet::Resource::TypeCollection.new("env") @code.add @top @code.add @middle end it "should create a resource instance" do @top.mk_plain_resource(@scope).should be_instance_of(Puppet::Parser::Resource) end it "should set its resource type to 'class' when it is a hostclass" do - Puppet::Parser::ResourceType.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class" + Puppet::Resource::Type.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class" end it "should set its resource type to 'node' when it is a node" do - Puppet::Parser::ResourceType.new(:node, "top").mk_plain_resource(@scope).type.should == "Node" + Puppet::Resource::Type.new(:node, "top").mk_plain_resource(@scope).type.should == "Node" end it "should fail when it is a definition" do - lambda { Puppet::Parser::ResourceType.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError) + lambda { Puppet::Resource::Type.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError) end it "should add the created resource to the scope's catalog" do @top.mk_plain_resource(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should evaluate the parent class if one exists" do @middle.mk_plain_resource(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should fail to evaluate if a parent class is defined but cannot be found" do - othertop = Puppet::Parser::ResourceType.new :hostclass, "something", :parent => "yay" + othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" @code.add othertop lambda { othertop.mk_plain_resource(@scope) }.should raise_error(Puppet::ParseError) end it "should not create a new resource if one already exists" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.mk_plain_resource(@scope) end it "should return the existing resource when not creating a new one" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.mk_plain_resource(@scope).should == "something" end it "should not create a new parent resource if one already exists and it has a parent class" do @top.mk_plain_resource(@scope) top_resource = @compiler.catalog.resource(:class, "top") @middle.mk_plain_resource(@scope) @compiler.catalog.resource(:class, "top").should equal(top_resource) end # #795 - tag before evaluation. it "should tag the catalog with the resource tags when it is evaluated" do @middle.mk_plain_resource(@scope) @compiler.catalog.should be_tagged("middle") end it "should tag the catalog with the parent class tags when it is evaluated" do @middle.mk_plain_resource(@scope) @compiler.catalog.should be_tagged("top") end end describe "when merging code from another instance" do def code(str) Puppet::Parser::AST::Leaf.new :value => str end it "should fail unless it is a class" do - lambda { Puppet::Parser::ResourceType.new(:node, "bar").merge("foo") }.should raise_error(ArgumentError) + lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(ArgumentError) end it "should fail unless the source instance is a class" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar") - source = Puppet::Parser::ResourceType.new(:node, "foo") + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:node, "foo") lambda { dest.merge(source) }.should raise_error(ArgumentError) end it "should fail if both classes have different parent classes" do - code = Puppet::Parser::ResourceTypeCollection.new("env") + code = Puppet::Resource::TypeCollection.new("env") {"a" => "b", "c" => "d"}.each do |parent, child| - code.add Puppet::Parser::ResourceType.new(:hostclass, parent) - code.add Puppet::Parser::ResourceType.new(:hostclass, child, :parent => parent) + code.add Puppet::Resource::Type.new(:hostclass, parent) + code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent) end lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(ArgumentError) end it "should copy the other class's parent if it has not parent" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar") + dest = Puppet::Resource::Type.new(:hostclass, "bar") - parent = Puppet::Parser::ResourceType.new(:hostclass, "parent") - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "parent") + parent = Puppet::Resource::Type.new(:hostclass, "parent") + source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent") dest.merge(source) dest.parent.should == "parent" end it "should copy the other class's documentation as its docs if it has no docs" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar") - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :doc => "yayness") + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "yayness" end it "should append the other class's docs to its docs if it has any" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :doc => "fooness") - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :doc => "yayness") + dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness") + source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "foonessyayness" end it "should turn its code into an ASTArray if necessary" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :code => code("foo")) - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => code("bar")) + dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo")) + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray) end it "should set the other class's code as its code if it has none" do - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar") - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => code("bar")) + dest = Puppet::Resource::Type.new(:hostclass, "bar") + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.value.should == "bar" end it "should append the other class's code to its code if it has any" do dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")] - dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :code => dcode) + dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")] - source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => scode) + source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode) dest.merge(source) dest.code.children.collect { |l| l.value }.should == %w{dest source} end end end diff --git a/spec/unit/parser/resource_type_collection.rb b/spec/unit/resource/type_collection.rb similarity index 71% rename from spec/unit/parser/resource_type_collection.rb rename to spec/unit/resource/type_collection.rb index b2f1588c9..2fc364d6b 100644 --- a/spec/unit/parser/resource_type_collection.rb +++ b/spec/unit/resource/type_collection.rb @@ -1,340 +1,340 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/parser/resource_type_collection' -require 'puppet/parser/resource_type' +require 'puppet/resource/type_collection' +require 'puppet/resource/type' -describe Puppet::Parser::ResourceTypeCollection do +describe Puppet::Resource::TypeCollection do before do - @instance = Puppet::Parser::ResourceType.new(:hostclass, "foo") - @code = Puppet::Parser::ResourceTypeCollection.new("env") + @instance = Puppet::Resource::Type.new(:hostclass, "foo") + @code = Puppet::Resource::TypeCollection.new("env") end it "should require an environment at initialization" do env = Puppet::Node::Environment.new("testing") - Puppet::Parser::ResourceTypeCollection.new(env).environment.should equal(env) + Puppet::Resource::TypeCollection.new(env).environment.should equal(env) end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") - Puppet::Parser::ResourceTypeCollection.new("testing").environment.should equal(env) + Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) end it "should be able to add a resource type" do - Puppet::Parser::ResourceTypeCollection.new("env").should respond_to(:add) + Puppet::Resource::TypeCollection.new("env").should respond_to(:add) end it "should consider '<<' to be an alias to 'add' but should return self" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:add).with "foo" loader.expects(:add).with "bar" loader << "foo" << "bar" end it "should set itself as the code collection for added resource types" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") - node = Puppet::Parser::ResourceType.new(:node, "foo") + node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) node.code_collection.should equal(@code) end it "should store node resource types as nodes" do - node = Puppet::Parser::ResourceType.new(:node, "foo") + node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) end it "should store hostclasses as hostclasses" do - klass = Puppet::Parser::ResourceType.new(:hostclass, "foo") + klass = Puppet::Resource::Type.new(:hostclass, "foo") @code.add(klass) @code.hostclass("foo").should equal(klass) end it "should store definitions as definitions" do - define = Puppet::Parser::ResourceType.new(:definition, "foo") + define = Puppet::Resource::Type.new(:definition, "foo") @code.add(define) @code.definition("foo").should equal(define) end %w{hostclass node definition}.each do |data| it "should have a method for adding a #{data}" do - Puppet::Parser::ResourceTypeCollection.new("env").should respond_to("add_" + data) + Puppet::Resource::TypeCollection.new("env").should respond_to("add_" + data) end it "should use the name of the instance to add it" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.send("add_#{data}", @instance) loader.send(data, @instance.name).should equal(@instance) end it "should fail to add a #{data} when one already exists" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.add @instance lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError) end it "should return the added #{data}" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.add(@instance).should equal(@instance) end it "should be able to retrieve #{data} by name" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(data, "bar") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "bar") loader.add instance loader.send(data, "bar").should equal(instance) end it "should retrieve #{data} insensitive to case" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(data, "Bar") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "Bar") loader.add instance loader.send(data, "bAr").should equal(instance) end it "should return nil when asked for a #{data} that has not been added" do - Puppet::Parser::ResourceTypeCollection.new("env").send(data, "foo").should be_nil + Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil end it "should be able to retrieve all #{data}s" do plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" } - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(data, "foo") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(data, "foo") loader.add instance loader.send(plurals[data]).should == { "foo" => instance } end end describe "when finding a qualified instance" do it "should return any found instance if the instance name is fully qualified" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find("namespace", "::foo::bar", :hostclass).should equal(instance) end it "should return nil if the instance name is fully qualified and no such instance exists" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.find("namespace", "::foo::bar", :hostclass).should be_nil end it "should return the partially qualified object if it exists in the provided namespace" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find("foo", "bar::baz", :hostclass).should equal(instance) end it "should return the unqualified object if it exists in the provided namespace" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find("foo", "bar", :hostclass).should equal(instance) end it "should return the unqualified object if it exists in the parent namespace" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find("foo::bar::baz", "bar", :hostclass).should equal(instance) end it "should should return the partially qualified object if it exists in the parent namespace" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find("foo::bar", "bar::baz", :hostclass).should equal(instance) end it "should return the qualified object if it exists in the root namespace" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find("foo::bar", "foo::bar::baz", :hostclass).should equal(instance) end it "should return nil if the object cannot be found" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz") + loader = Puppet::Resource::TypeCollection.new("env") + instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find("foo::bar", "eh", :hostclass).should be_nil end end it "should use the generic 'find' method with an empty namespace to find nodes" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find).with("", "bar", :node) loader.find_node("bar") end it "should use the generic 'find' method to find hostclasses" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find).with("foo", "bar", :hostclass) loader.find_hostclass("foo", "bar") end it "should use the generic 'find' method to find definitions" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") + loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find).with("foo", "bar", :definition) loader.find_definition("foo", "bar") end it "should indicate whether any nodes are defined" do - loader = Puppet::Parser::ResourceTypeCollection.new("env") - loader.add_node(Puppet::Parser::ResourceType.new(:node, "foo")) + loader = Puppet::Resource::TypeCollection.new("env") + loader.add_node(Puppet::Resource::Type.new(:node, "foo")) loader.should be_nodes end it "should indicate whether no nodes are defined" do - Puppet::Parser::ResourceTypeCollection.new("env").should_not be_nodes + Puppet::Resource::TypeCollection.new("env").should_not be_nodes end describe "when finding nodes" do before :each do - @loader = Puppet::Parser::ResourceTypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new("env") end it "should return any node whose name exactly matches the provided node name" do - node = Puppet::Parser::ResourceType.new(:node, "foo") + node = Puppet::Resource::Type.new(:node, "foo") @loader << node @loader.node("foo").should equal(node) end it "should return the first regex node whose regex matches the provided node name" do - node1 = Puppet::Parser::ResourceType.new(:node, /\w/) - node2 = Puppet::Parser::ResourceType.new(:node, /\d/) + node1 = Puppet::Resource::Type.new(:node, /\w/) + node2 = Puppet::Resource::Type.new(:node, /\d/) @loader << node1 << node2 @loader.node("foo10").should equal(node1) end it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do - node1 = Puppet::Parser::ResourceType.new(:node, /\w/) - node2 = Puppet::Parser::ResourceType.new(:node, "foo") + node1 = Puppet::Resource::Type.new(:node, /\w/) + node2 = Puppet::Resource::Type.new(:node, "foo") @loader << node1 << node2 @loader.node("foo").should equal(node2) end end describe "when managing files" do before do - @loader = Puppet::Parser::ResourceTypeCollection.new("env") + @loader = Puppet::Resource::TypeCollection.new("env") Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") end it "should have a method for specifying a file should be watched" do @loader.should respond_to(:watch_file) end it "should have a method for determining if a file is being watched" do @loader.watch_file("/foo/bar") @loader.should be_watching_file("/foo/bar") end it "should use LoadedFile to watch files" do Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") @loader.watch_file("/foo/bar") end it "should be considered stale if any files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => true Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should be_stale end it "should not be considered stable if no files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => false Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should_not be_stale end end describe "when performing initial import" do before do @parser = stub 'parser', :file= => nil, :string => nil, :parse => nil Puppet::Parser::Parser.stubs(:new).returns @parser - @code = Puppet::Parser::ResourceTypeCollection.new("env") + @code = Puppet::Resource::TypeCollection.new("env") end it "should create a new parser instance" do Puppet::Parser::Parser.expects(:new).returns @parser @code.perform_initial_import end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) @code.perform_initial_import end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do File.expects(:exist?).with("/my/file").returns true Puppet.settings[:manifest] = "/my/file" @parser.expects(:file=).with "/my/file" @parser.expects(:parse) @code.perform_initial_import end it "should not attempt to load a manifest if none is present" do File.expects(:exist?).with("/my/file").returns false Puppet.settings[:manifest] = "/my/file" @parser.expects(:file=).never @parser.expects(:parse).never @code.perform_initial_import end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true @parser.expects(:parse).raises ArgumentError lambda { @code.perform_initial_import }.should raise_error(Puppet::Error) end end describe "when determining the configuration version" do before do - @code = Puppet::Parser::ResourceTypeCollection.new("env") + @code = Puppet::Resource::TypeCollection.new("env") end it "should default to the current time" do time = Time.now Time.stubs(:now).returns time @code.version.should == time.to_i end it "should use the output of the environment's config_version setting if one is provided" do @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n" @code.version.should == "output" end it "should raise a puppet parser error if executing config_version fails" do @code.environment.stubs(:[]).with(:config_version).returns("test") Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) lambda { @code.version }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/parser/resource_type_collection_helper.rb b/spec/unit/resource/type_collection_helper.rb similarity index 74% rename from spec/unit/parser/resource_type_collection_helper.rb rename to spec/unit/resource/type_collection_helper.rb index c93c9f20e..e390ff952 100644 --- a/spec/unit/parser/resource_type_collection_helper.rb +++ b/spec/unit/resource/type_collection_helper.rb @@ -1,25 +1,25 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/parser/resource_type_collection_helper' +require 'puppet/resource/type_collection_helper' class RTCHelperTester - include Puppet::Parser::ResourceTypeCollectionHelper + include Puppet::Resource::TypeCollectionHelper end -describe Puppet::Parser::ResourceTypeCollectionHelper do +describe Puppet::Resource::TypeCollectionHelper do before do @helper = RTCHelperTester.new end it "should use its current environment to retrieve the known resource type collection" do env = stub 'environment' @helper.expects(:environment).returns env rtc = stub 'known_resource_types' env.expects(:known_resource_types).returns rtc @helper.known_resource_types.should equal(rtc) end end diff --git a/spec/unit/util/rdoc/parser.rb b/spec/unit/util/rdoc/parser.rb index c62b01d43..7113b9539 100755 --- a/spec/unit/util/rdoc/parser.rb +++ b/spec/unit/util/rdoc/parser.rb @@ -1,540 +1,540 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } -require 'puppet/parser/resource_type_collection' +require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' describe RDoc::Parser do before :each do File.stubs(:stat).with("init.pp") @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) end describe "when scanning files" do it "should parse puppet files with the puppet parser" do @parser.stubs(:scan_top_level) parser = stub 'parser' Puppet::Parser::Parser.expects(:new).returns(parser) parser.expects(:parse) parser.expects(:file=).with("module/manifests/init.pp") @parser.scan end it "should scan the ast for Puppet files" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) @parser.expects(:scan_top_level) @parser.scan end it "should return a PuppetTopLevel to RDoc" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) @parser.expects(:scan_top_level) @parser.scan.should be_a(RDoc::PuppetTopLevel) end end describe "when scanning top level entities" do before :each do @resource_type_collection = stub_everything 'resource_type_collection' @parser.ast = @resource_type_collection @parser.stubs(:split_module).returns("module") @topcontainer = stub_everything 'topcontainer' @container = stub_everything 'container' @module = stub_everything 'module' @container.stubs(:add_module).returns(@module) @parser.stubs(:get_class_or_module).returns([@container, "module"]) end it "should read any present README as module documentation" do FileTest.stubs(:readable?).returns(true) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme") @parser.scan_top_level(@topcontainer) end it "should tell the container its module name" do @parser.stubs(:parse_elements) @topcontainer.expects(:module_name=).with("module") @parser.scan_top_level(@topcontainer) end it "should not document our toplevel if it isn't a valid module" do @parser.stubs(:split_module).returns(nil) @topcontainer.expects(:document_self=).with(false) @parser.expects(:parse_elements).never @parser.scan_top_level(@topcontainer) end it "should set the module as global if we parse the global manifests (ie module)" do @parser.stubs(:split_module).returns("") @parser.stubs(:parse_elements) @topcontainer.expects(:global=).with(true) @parser.scan_top_level(@topcontainer) end it "should attach this module container to the toplevel container" do @parser.stubs(:parse_elements) @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) @parser.scan_top_level(@topcontainer) end it "should defer ast parsing to parse_elements for this module" do @parser.expects(:parse_elements).with(@module) @parser.scan_top_level(@topcontainer) end it "should defer plugins parsing to parse_plugins for this module" do @parser.input_file_name = "module/plugins/puppet/parser/function.rb" @parser.expects(:parse_plugins).with(@module) @parser.scan_top_level(@topcontainer) end end describe "when finding modules from filepath" do before :each do Puppet::Module.stubs(:modulepath).returns("/path/to/modules") end it "should return the module name for modulized puppet manifests" do File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" end it "should return for manifests not under module path" do File.stubs(:expand_path).returns("/path/to/manifests/init.pp") File.stubs(:identical?).returns(false) @parser.split_module("/path/to/manifests/init.pp").should == "" end end describe "when parsing AST elements" do before :each do @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" - @resource_type_collection = Puppet::Parser::ResourceTypeCollection.new("env") + @resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser.ast = @resource_type_collection @container = stub_everything 'container' end it "should document classes in the parsed file" do @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container) @parser.parse_elements(@container) end it "should not document class parsed in an other file" do @klass.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container).never @parser.parse_elements(@container) end it "should document vardefs for the main class" do @klass.stubs(:name).returns :main @resource_type_collection.add_hostclass(@klass) code = stub 'code', :is_a? => false @klass.stubs(:name).returns("") @klass.stubs(:code).returns(code) @parser.expects(:scan_for_vardef).with(@container, code) @parser.parse_elements(@container) end it "should document definitions in the parsed file" do @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container) @parser.parse_elements(@container) end it "should not document definitions parsed in an other file" do @definition.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container).never @parser.parse_elements(@container) end it "should document nodes in the parsed file" do @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container) @parser.parse_elements(@container) end it "should not document node parsed in an other file" do @node.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container).never @parser.parse_elements(@container) end end describe "when documenting definition" do before(:each) do @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 @class = stub_everything 'class' @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) end it "should register a RDoc method to the current container" do @class.expects(:add_method).with { |m| m.name == "mydef"} @parser.document_define("mydef", @define, @class) end it "should attach the documentation to this method" do @class.expects(:add_method).with { |m| m.comment = "mydoc" } @parser.document_define("mydef", @define, @class) end it "should produce a better error message on unhandled exception" do @class.expects(:add_method).raises(ArgumentError) lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end it "should convert all definition parameter to string" do arg = stub 'arg' val = stub 'val' @define.stubs(:arguments).returns({arg => val}) arg.expects(:to_s).returns("arg") val.expects(:to_s).returns("val") @parser.document_define("mydef", @define, @class) end end describe "when documenting nodes" do before :each do @code = stub_everything 'code' @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_node = stub_everything 'rdocnode' @class = stub_everything 'class' @class.stubs(:add_node).returns(@rdoc_node) end it "should add a node to the current container" do @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) @parser.document_node("mynode", @node, @class) end it "should associate the node documentation to the rdoc node" do @rdoc_node.expects(:comment=).with("mydoc") @parser.document_node("mynode", @node, @class) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for variable definition" do @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should produce a better error message on unhandled exception" do @class.stubs(:add_node).raises(ArgumentError) lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when documenting classes" do before :each do @code = stub_everything 'code' @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_class = stub_everything 'rdoc-class' @module = stub_everything 'class' @module.stubs(:add_class).returns(@rdoc_class) @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) end it "should add a class to the current container" do @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) @parser.document_class("mynode", @class, @module) end it "should set the superclass" do @rdoc_class.expects(:superclass=).with("parent") @parser.document_class("mynode", @class, @module) end it "should associate the node documentation to the rdoc class" do @rdoc_class.expects(:comment=).with("mydoc") @parser.document_class("mynode", @class, @module) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should produce a better error message on unhandled exception" do @module.stubs(:add_class).raises(ArgumentError) lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when scanning for includes and requires" do def create_stmt(name) stmt_value = stub "#{name}_value", :value => "myclass" stmt = stub_everything 'stmt', :name => name, :arguments => [stmt_value], :doc => "mydoc" stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) stmt end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class,create_stmt("include")) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt("include") ]) @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end it "should register requires to the current container" do @code.stubs(:children).returns([ create_stmt("require") ]) @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end end describe "when scanning for realized virtual resources" do def create_stmt stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" stmt = stub_everything 'stmt', :name => "realize", :arguments => [stmt_value], :doc => "mydoc" stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) stmt.stubs(:is_a?).with(Puppet::Parser::AST::Function).returns(true) stmt end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class,create_stmt()) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt() ]) @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class, [@code]) end end describe "when scanning for variable definition" do before :each do @class = stub_everything 'class' @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should recursively register variables to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, @stmt) end end describe "when scanning for resources" do before :each do @class = stub_everything 'class' param = stub 'params', :children => [] @stmt = stub_everything 'stmt', :type => "File", :title => "myfile", :doc => "mydoc", :params => param @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) @stmt.stubs(:is_a?).with(Puppet::Parser::AST::Resource).returns(true) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should register a PuppetResource to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, @stmt) end end describe "when parsing plugins" do before :each do @container = stub 'container' end it "should delegate parsing custom facts to parse_facts" do @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_fact).with(@container) @parser.parse_plugins(@container) end it "should delegate parsing plugins to parse_plugins" do @parser = RDoc::Parser.new(@top_level, "module/manifests/plugins/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_puppet_plugin).with(@container) @parser.parse_plugins(@container) end end describe "when parsing plugins" do before :each do @container = stub_everything 'container' end it "should add custom functions to the container" do File.stubs(:open).yields("# documentation module Puppet::Parser::Functions newfunction(:myfunc, :type => :rvalue) do |args| File.dirname(args[0]) end end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "myfunc" end @parser.parse_puppet_plugin(@container) end it "should add custom types to the container" do File.stubs(:open).yields("# documentation Puppet::Type.newtype(:mytype) do end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "mytype" end @parser.parse_puppet_plugin(@container) end end describe "when parsing facts" do before :each do @container = stub_everything 'container' File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) end it "should add facts to the container" do @container.expects(:add_fact).with do |fact| fact.comment == "documentation\n" and fact.name == "myfact" end @parser.parse_fact(@container) end it "should add confine to the parsed facts" do ourfact = nil @container.expects(:add_fact).with do |fact| ourfact = fact true end @parser.parse_fact(@container) ourfact.confine.should == { :type => "kernel", :value => ":linux" } end end end diff --git a/test/language/parser.rb b/test/language/parser.rb index 6c68184a6..a3311f423 100755 --- a/test/language/parser.rb +++ b/test/language/parser.rb @@ -1,1215 +1,1215 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../lib/puppettest' require 'mocha' require 'puppet' require 'puppet/parser/parser' require 'puppettest' require 'puppettest/support/utils' class TestParser < Test::Unit::TestCase include PuppetTest include PuppetTest::ParserTesting include PuppetTest::Support::Utils def setup super Puppet[:parseonly] = true #@lexer = Puppet::Parser::Lexer.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 } } end def test_failers failers { |file| parser = mkparser Puppet.debug("parsing failer %s" % file) if __FILE__ == $0 assert_raise(Puppet::ParseError, "Did not fail while parsing %s" % file) { parser.file = file ast = parser.parse config = mkcompiler(parser) config.compile #ast.hostclass("").evaluate config.topscope } } end def test_arrayrvalues parser = mkparser ret = nil file = tempfile() assert_nothing_raised { parser.string = "file { \"#{file}\": mode => [755, 640] }" } assert_nothing_raised { ret = parser.parse } end def test_arrayrvalueswithtrailingcomma parser = mkparser ret = nil file = tempfile() assert_nothing_raised { parser.string = "file { \"#{file}\": mode => [755, 640,] }" } assert_nothing_raised { ret = parser.parse } end def mkmanifest(file) name = File.join(tmpdir, "file%s" % rand(100)) @@tmpfiles << name File.open(file, "w") { |f| f.puts "file { \"%s\": ensure => file, mode => 755 }\n" % name } end def test_importglobbing basedir = File.join(tmpdir(), "importesting") @@tmpfiles << basedir Dir.mkdir(basedir) subdir = "subdir" Dir.mkdir(File.join(basedir, subdir)) manifest = File.join(basedir, "manifest") File.open(manifest, "w") { |f| f.puts "import \"%s/*\"" % subdir } 4.times { |i| path = File.join(basedir, subdir, "subfile%s" % i) mkmanifest(path) } assert_nothing_raised("Could not parse multiple files") { parser = mkparser parser.file = manifest parser.parse } end def test_nonexistent_import basedir = File.join(tmpdir(), "importesting") @@tmpfiles << basedir Dir.mkdir(basedir) manifest = File.join(basedir, "manifest") File.open(manifest, "w") do |f| f.puts "import \" no such file \"" end assert_raise(Puppet::ParseError) { parser = mkparser parser.file = manifest parser.parse } end def test_trailingcomma path = tempfile() str = %{file { "#{path}": ensure => file, } } parser = mkparser parser.string = str assert_nothing_raised("Could not parse trailing comma") { parser.parse } end def test_importedclasses imported = tempfile() importer = tempfile() made = tempfile() File.open(imported, "w") do |f| f.puts %{class foo { file { "#{made}": ensure => file }}} end File.open(importer, "w") do |f| f.puts %{import "#{imported}"\ninclude foo} end parser = mkparser parser.file = importer # Make sure it parses fine assert_nothing_raised { parser.parse } # Now make sure it actually does the work assert_creates(importer, made) end # Make sure fully qualified and unqualified files can be imported def test_fqfilesandlocalfiles dir = tempfile() Dir.mkdir(dir) importer = File.join(dir, "site.pp") fullfile = File.join(dir, "full.pp") localfile = File.join(dir, "local.pp") files = [] File.open(importer, "w") do |f| f.puts %{import "#{fullfile}"\ninclude full\nimport "local.pp"\ninclude local} end fullmaker = tempfile() files << fullmaker File.open(fullfile, "w") do |f| f.puts %{class full { file { "#{fullmaker}": ensure => file }}} end localmaker = tempfile() files << localmaker File.open(localfile, "w") do |f| f.puts %{class local { file { "#{localmaker}": ensure => file }}} end parser = mkparser parser.file = importer # Make sure it parses assert_nothing_raised { parser.parse } # Now make sure it actually does the work assert_creates(importer, *files) end # Make sure the parser adds '.pp' when necessary def test_addingpp dir = tempfile() Dir.mkdir(dir) importer = File.join(dir, "site.pp") localfile = File.join(dir, "local.pp") files = [] File.open(importer, "w") do |f| f.puts %{import "local"\ninclude local} end file = tempfile() files << file File.open(localfile, "w") do |f| f.puts %{class local { file { "#{file}": ensure => file }}} end parser = mkparser parser.file = importer assert_nothing_raised { parser.parse } end # Make sure that file importing changes file relative names. def test_changingrelativenames dir = tempfile() Dir.mkdir(dir) Dir.mkdir(File.join(dir, "subdir")) top = File.join(dir, "site.pp") subone = File.join(dir, "subdir/subone") subtwo = File.join(dir, "subdir/subtwo") files = [] file = tempfile() files << file File.open(subone + ".pp", "w") do |f| f.puts %{class one { file { "#{file}": ensure => file }}} end otherfile = tempfile() files << otherfile File.open(subtwo + ".pp", "w") do |f| f.puts %{import "subone"\n class two inherits one { file { "#{otherfile}": ensure => file } }} end File.open(top, "w") do |f| f.puts %{import "subdir/subtwo"} end parser = mkparser parser.file = top assert_nothing_raised { parser.parse } end # Defaults are purely syntactical, so it doesn't make sense to be able to # collect them. def test_uncollectabledefaults string = "@Port { protocols => tcp }" assert_raise(Puppet::ParseError) { mkparser.parse(string) } end # Verify that we can parse collections def test_collecting text = "Port <| |>" parser = mkparser parser.string = text ret = nil assert_nothing_raised { ret = parser.parse } ret.hostclass("").code.each do |obj| assert_instance_of(AST::Collection, obj) end end def test_emptyfile file = tempfile() File.open(file, "w") do |f| f.puts %{} end parser = mkparser parser.file = file assert_nothing_raised { parser.parse } end def test_multiple_nodes_named file = tempfile() other = tempfile() File.open(file, "w") do |f| f.puts %{ node nodeA, nodeB { file { "#{other}": ensure => file } } } end parser = mkparser parser.file = file ast = nil assert_nothing_raised { ast = parser.parse } end def test_emptyarrays str = %{$var = []\n} parser = mkparser parser.string = str # Make sure it parses fine assert_nothing_raised { parser.parse } end # Make sure function names aren't reserved words. def test_functionnamecollision str = %{tag yayness tag(rahness) file { "/tmp/yayness": tag => "rahness", ensure => exists } } parser = mkparser parser.string = str # Make sure it parses fine assert_nothing_raised { parser.parse } end def test_metaparams_in_definition_prototypes parser = mkparser assert_raise(Puppet::ParseError) { parser.parse %{define mydef($schedule) {}} } assert_nothing_raised { parser.parse %{define adef($schedule = false) {}} parser.parse %{define mydef($schedule = daily) {}} } end def test_parsingif parser = mkparser exec = proc do |val| %{exec { "/bin/echo #{val}": logoutput => true }} end str1 = %{if true { #{exec.call("true")} }} ret = nil assert_nothing_raised { ret = parser.parse(str1).hostclass("").code[0] } assert_instance_of(Puppet::Parser::AST::IfStatement, ret) parser = mkparser str2 = %{if true { #{exec.call("true")} } else { #{exec.call("false")} }} assert_nothing_raised { ret = parser.parse(str2).hostclass("").code[0] } assert_instance_of(Puppet::Parser::AST::IfStatement, ret) assert_instance_of(Puppet::Parser::AST::Else, ret.else) end def test_hostclass parser = mkparser assert_nothing_raised { parser.parse %{class myclass { class other {} }} } assert(parser.hostclass("myclass"), "Could not find myclass") assert(parser.hostclass("myclass::other"), "Could not find myclass::other") assert_nothing_raised { parser.parse "class base {} class container { class deep::sub inherits base {} }" } sub = parser.hostclass("container::deep::sub") assert(sub, "Could not find sub") # Now try it with a parent class being a fq class assert_nothing_raised { parser.parse "class container::one inherits container::deep::sub {}" } sub = parser.hostclass("container::one") assert(sub, "Could not find one") assert_equal("container::deep::sub", sub.parentclass) # Finally, try including a qualified class assert_nothing_raised("Could not include fully qualified class") { parser.parse "include container::deep::sub" } end def test_topnamespace parser = mkparser # Make sure we put the top-level code into a class called "" in # the "" namespace assert_nothing_raised do out = parser.parse "" - assert_instance_of(Puppet::Parser::ResourceTypeCollection, out) + assert_instance_of(Puppet::Resource::TypeCollection, out) assert_nil(parser.hostclass(""), "Got a 'main' class when we had no code") end # Now try something a touch more complicated parser.initvars assert_nothing_raised do out = parser.parse "Exec { path => '/usr/bin:/usr/sbin' }" - assert_instance_of(Puppet::Parser::ResourceTypeCollection, out) + assert_instance_of(Puppet::Resource::TypeCollection, out) assert_equal("", parser.hostclass("").classname) assert_equal("", parser.hostclass("").namespace) end end # Make sure virtual and exported resources work appropriately. def test_virtualresources tests = [:virtual] if Puppet.features.rails? catalog_cache_class = Puppet::Resource::Catalog.indirection.cache_class facts_cache_class = Puppet::Node::Facts.indirection.cache_class node_cache_class = Puppet::Node.indirection.cache_class Puppet[:storeconfigs] = true tests << :exported end tests.each do |form| parser = mkparser if form == :virtual at = "@" else at = "@@" end check = proc do |res, msg| if res.is_a?(Puppet::Parser::Resource) txt = res.ref else txt = res.class end # Real resources get marked virtual when exported if form == :virtual or res.is_a?(Puppet::Parser::Resource) assert(res.virtual, "#{msg} #{at}#{txt} is not virtual") end if form == :virtual assert(! res.exported, "#{msg} #{at}#{txt} is exported") else assert(res.exported, "#{msg} #{at}#{txt} is not exported") end end ret = nil assert_nothing_raised do ret = parser.parse("#{at}file { '/tmp/testing': owner => root }") end assert_instance_of(AST::ASTArray, ret.hostclass("").code) resdef = ret.hostclass("").code[0] assert_instance_of(AST::Resource, resdef) assert_equal("/tmp/testing", resdef.title.value) # We always get an astarray back, so... check.call(resdef, "simple resource") # 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 ret.hostclass("").each do |res| assert_instance_of(AST::Resource, res) check.call(res, "multiresource") end end ensure if Puppet.features.rails? Puppet[:storeconfigs] = false Puppet::Resource::Catalog.cache_class = catalog_cache_class Puppet::Node::Facts.cache_class = facts_cache_class Puppet::Node.cache_class = node_cache_class end end def test_collections tests = [:virtual] if Puppet.features.rails? catalog_cache_class = Puppet::Resource::Catalog.indirection.cache_class facts_cache_class = Puppet::Node::Facts.indirection.cache_class node_cache_class = Puppet::Node.indirection.cache_class Puppet[:storeconfigs] = true tests << :exported end tests.each do |form| parser = mkparser if form == :virtual arrow = "<||>" else arrow = "<<||>>" end ret = nil assert_nothing_raised do ret = parser.parse("File #{arrow}") end coll = ret.hostclass("").code[0] assert_instance_of(AST::Collection, coll) assert_equal(form, coll.form) end ensure if Puppet.features.rails? Puppet[:storeconfigs] = false Puppet::Resource::Catalog.cache_class = catalog_cache_class Puppet::Node::Facts.cache_class = facts_cache_class Puppet::Node.cache_class = node_cache_class 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).hostclass("").code[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).hostclass("").code[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).hostclass("").code[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 # We've had problems with files other than site.pp importing into main. def test_importing_into_main top = tempfile() other = tempfile() File.open(top, "w") do |f| f.puts "import '#{other}'" end file = tempfile() File.open(other, "w") do |f| f.puts "file { '#{file}': ensure => present }" end Puppet[:manifest] = top interp = Puppet::Parser::Interpreter.new code = nil assert_nothing_raised do code = interp.compile(mknode).extract.flatten end assert(code.length == 1, "Did not get the file") assert_instance_of(Puppet::TransObject, code[0]) end def test_fully_qualified_definitions parser = mkparser assert_nothing_raised("Could not parse fully-qualified definition") { parser.parse %{define one::two { }} } assert(parser.definition("one::two"), "Could not find one::two with no namespace") # Now try using the definition assert_nothing_raised("Could not parse fully-qualified definition usage") { parser.parse %{one::two { yayness: }} } end # #524 def test_functions_with_no_arguments parser = mkparser assert_nothing_raised("Could not parse statement function with no args") { parser.parse %{tag()} } assert_nothing_raised("Could not parse rvalue function with no args") { parser.parse %{$testing = template()} } end # #774 def test_fully_qualified_collection_statement parser = mkparser assert_nothing_raised("Could not parse fully qualified collection statement") { parser.parse %{Foo::Bar <||>} } end def test_module_import basedir = File.join(tmpdir(), "module-import") @@tmpfiles << basedir Dir.mkdir(basedir) modfiles = [ "init.pp", "mani1.pp", "mani2.pp", "sub/smani1.pp", "sub/smani2.pp" ] modpath = File.join(basedir, "modules") Puppet[:modulepath] = modpath modname = "amod" manipath = File::join(modpath, modname, Puppet::Module::MANIFESTS) FileUtils::mkdir_p(File::join(manipath, "sub")) targets = [] modfiles.each do |fname| target = File::join(basedir, File::basename(fname, '.pp')) targets << target txt = %[ file { '#{target}': content => "#{fname}" } ] if fname == "init.pp" txt = %[import 'mani1' \nimport '#{modname}/mani2'\nimport '#{modname}/sub/*.pp'\n ] + txt end File::open(File::join(manipath, fname), "w") do |f| f.puts txt end end manifest_texts = [ "import '#{modname}'", "import '#{modname}/init'", "import '#{modname}/init.pp'" ] manifest = File.join(modpath, "manifest.pp") manifest_texts.each do |txt| File.open(manifest, "w") { |f| f.puts txt } assert_nothing_raised { parser = mkparser parser.file = manifest parser.parse } assert_creates(manifest, *targets) end end # #544 def test_ignoreimports parser = mkparser assert(! Puppet[:ignoreimport], ":ignoreimport defaulted to true") assert_raise(Puppet::ParseError, "Did not fail on missing import") do parser.parse("import 'nosuchfile'") end assert_nothing_raised("could not set :ignoreimport") do Puppet[:ignoreimport] = true end assert_nothing_raised("Parser did not follow :ignoreimports") do parser.parse("import 'nosuchfile'") end end def test_multiple_imports_on_one_line one = tempfile two = tempfile base = tempfile File.open(one, "w") { |f| f.puts "$var = value" } File.open(two, "w") { |f| f.puts "$var = value" } File.open(base, "w") { |f| f.puts "import '#{one}', '#{two}'" } parser = mkparser parser.file = base # Importing is logged at debug time. Puppet::Util::Log.level = :debug assert_nothing_raised("Parser could not import multiple files at once") do parser.parse end [one, two].each do |file| assert(@logs.detect { |l| l.message =~ /importing '#{file}'/}, "did not import %s" % file) end end def test_cannot_assign_qualified_variables parser = mkparser assert_raise(Puppet::ParseError, "successfully assigned a qualified variable") do parser.parse("$one::two = yay") end end # #588 def test_globbing_with_directories dir = tempfile Dir.mkdir(dir) subdir = File.join(dir, "subdir") Dir.mkdir(subdir) file = File.join(dir, "file.pp") maker = tempfile File.open(file, "w") { |f| f.puts "file { '#{maker}': ensure => file }" } parser = mkparser assert_nothing_raised("Globbing failed when it matched a directory") do parser.import("%s/*" % dir) end end # #629 - undef keyword def test_undef parser = mkparser result = nil assert_nothing_raised("Could not parse assignment to undef") { result = parser.parse %{$variable = undef} } main = result.hostclass("").code children = main.children assert_instance_of(AST::VarDef, main.children[0]) assert_instance_of(AST::Undef, main.children[0].value) end # Prompted by #729 -- parsing should not modify the interpreter. def test_parse parser = mkparser str = "file { '/tmp/yay': ensure => file }\nclass yay {}\nnode foo {}\ndefine bar {}\n" result = nil assert_nothing_raised("Could not parse") do result = parser.parse(str) end - assert_instance_of(Puppet::Parser::ResourceTypeCollection, result, "Did not get a ASTSet back from parsing") + assert_instance_of(Puppet::Resource::TypeCollection, result, "Did not get a ASTSet back from parsing") assert_instance_of(AST::HostClass, result.hostclass("yay"), "Did not create 'yay' class") assert_instance_of(AST::HostClass, result.hostclass(""), "Did not create main class") assert_instance_of(AST::Definition, result.definition("bar"), "Did not create 'bar' definition") assert_instance_of(AST::Node, result.node("foo"), "Did not create 'foo' node") end # Make sure our node gets added to the node table. def test_newnode parser = mkparser # First just try calling it directly assert_nothing_raised { parser.newnode("mynode", :code => :yay) } assert_equal(:yay, parser.node("mynode").code) # Now make sure that trying to redefine it throws an error. assert_raise(Puppet::ParseError) { parser.newnode("mynode", {}) } # Now try one with no code assert_nothing_raised { parser.newnode("simplenode", :parent => :foo) } # Now define the parent node parser.newnode(:foo) # And make sure we get things back correctly assert_equal(:foo, parser.node("simplenode").parentclass) assert_nil(parser.node("simplenode").code) # Now make sure that trying to redefine it throws an error. assert_raise(Puppet::ParseError) { parser.newnode("mynode", {}) } # Test multiple names names = ["one", "two", "three"] assert_nothing_raised { parser.newnode(names, {:code => :yay, :parent => :foo}) } names.each do |name| assert_equal(:yay, parser.node(name).code) assert_equal(:foo, parser.node(name).parentclass) # Now make sure that trying to redefine it throws an error. assert_raise(Puppet::ParseError) { parser.newnode(name, {}) } end end def test_newdefine parser = mkparser assert_nothing_raised { parser.newdefine("mydefine", :code => :yay, :arguments => ["a", stringobj("b")]) } mydefine = parser.definition("mydefine") assert(mydefine, "Could not find definition") assert_equal("", mydefine.namespace) assert_equal("mydefine", mydefine.classname) assert_raise(Puppet::ParseError) do parser.newdefine("mydefine", :code => :yay, :arguments => ["a", stringobj("b")]) end # Now define the same thing in a different scope assert_nothing_raised { parser.newdefine("other::mydefine", :code => :other, :arguments => ["a", stringobj("b")]) } other = parser.definition("other::mydefine") assert(other, "Could not find definition") assert(parser.definition("other::mydefine"), "Could not find other::mydefine") assert_equal(:other, other.code) assert_equal("other", other.namespace) assert_equal("other::mydefine", other.classname) end def test_newclass scope = mkscope parser = scope.compiler.parser mkcode = proc do |ary| classes = ary.collect do |string| AST::FlatString.new(:value => string) end AST::ASTArray.new(:children => classes) end # First make sure that code is being appended code = mkcode.call(%w{original code}) klass = nil assert_nothing_raised { klass = parser.newclass("myclass", :code => code) } assert(klass, "Did not return class") assert(parser.hostclass("myclass"), "Could not find definition") assert_equal("myclass", parser.hostclass("myclass").classname) assert_equal(%w{original code}, parser.hostclass("myclass").code.evaluate(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 = parser.newclass("myclass", :code => code) end assert(klass, "Did not return class when appending") assert_equal(%w{original code something new}, parser.hostclass("myclass").code.evaluate(scope)) # Now create the same class name in a different scope assert_nothing_raised { klass = parser.newclass("other::myclass", :code => mkcode.call(%w{something diff})) } assert(klass, "Did not return class") other = parser.hostclass("other::myclass") assert(other, "Could not find class") assert_equal("other::myclass", other.classname) assert_equal("other::myclass", other.namespace) assert_equal(%w{something diff}, other.code.evaluate(scope)) # Make sure newclass deals correctly with nodes with no code klass = parser.newclass("nocode") assert(klass, "Did not return class") assert_nothing_raised do klass = parser.newclass("nocode", :code => mkcode.call(%w{yay test})) end assert(klass, "Did not return class with no code") assert_equal(%w{yay test}, parser.hostclass("nocode").code.evaluate(scope)) # Then try merging something into nothing parser.newclass("nocode2", :code => mkcode.call(%w{foo test})) assert(klass, "Did not return class with no code") assert_nothing_raised do klass = parser.newclass("nocode2") end assert(klass, "Did not return class with no code") assert_equal(%w{foo test}, parser.hostclass("nocode2").code.evaluate(scope)) # And lastly, nothing and nothing klass = parser.newclass("nocode3") assert(klass, "Did not return class with no code") assert_nothing_raised do klass = parser.newclass("nocode3") end assert(klass, "Did not return class with no code") assert_nil(parser.hostclass("nocode3").code) end # Make sure you can't have classes and defines with the same name in the # same scope. def test_classes_beat_defines parser = mkparser assert_nothing_raised { parser.newclass("yay::funtest") } assert_raise(Puppet::ParseError) do parser.newdefine("yay::funtest") end assert_nothing_raised { parser.newdefine("yay::yaytest") } assert_raise(Puppet::ParseError) do parser.newclass("yay::yaytest") end end def test_namesplit parser = mkparser assert_nothing_raised do {"base::sub" => %w{base sub}, "main" => ["", "main"], "one::two::three::four" => ["one::two::three", "four"], }.each do |name, ary| result = parser.namesplit(name) assert_equal(ary, result, "%s split to %s" % [name, result]) end end end # Now make sure we get appropriate behaviour with parent class conflicts. def test_newclass_parentage parser = mkparser parser.newclass("base1") parser.newclass("one::two::three") # First create it with no parentclass. assert_nothing_raised { parser.newclass("sub") } assert(parser.hostclass("sub"), "Could not find definition") assert_nil(parser.hostclass("sub").parentclass) # Make sure we can't set the parent class to ourself. assert_raise(Puppet::ParseError) { parser.newclass("sub", :parent => "sub") } # Now create another one, with a parentclass. assert_nothing_raised { parser.newclass("sub", :parent => "base1") } # Make sure we get the right parent class, and make sure it's not an object. assert_equal("base1", parser.hostclass("sub").parentclass) # Now make sure we get a failure if we try to conflict. assert_raise(Puppet::ParseError) { parser.newclass("sub", :parent => "one::two::three") } # Make sure that failure didn't screw us up in any way. assert_equal("base1", parser.hostclass("sub").parentclass) # But make sure we can create a class with a fq parent assert_nothing_raised { parser.newclass("another", :parent => "one::two::three") } assert_equal("one::two::three", parser.hostclass("another").parentclass) end # Setup a module. def mk_module(name, files = {}) mdir = File.join(@dir, name) mandir = File.join(mdir, "manifests") FileUtils.mkdir_p mandir if defs = files[:define] files.delete(:define) end Dir.chdir(mandir) do files.each do |file, classes| File.open("%s.pp" % file, "w") do |f| classes.each { |klass| if defs f.puts "define %s {}" % klass else f.puts "class %s {}" % klass end } end end end end # #596 - make sure classes and definitions load automatically if they're in modules, so we don't have to manually load each one. def test_module_autoloading @dir = tempfile Puppet[:modulepath] = @dir FileUtils.mkdir_p @dir parser = mkparser # Make sure we fail like normal for actually missing classes assert_nil(parser.find_hostclass("", "nosuchclass"), "Did not return nil on missing classes") # test the simple case -- the module class itself name = "simple" mk_module(name, :init => [name]) # Try to load the module automatically now klass = parser.find_hostclass("", name) assert_instance_of(AST::HostClass, klass, "Did not autoload class from module init file") assert_equal(name, klass.classname, "Incorrect class was returned") # Try loading the simple module when we're in something other than the base namespace. parser = mkparser klass = parser.find_hostclass("something::else", name) assert_instance_of(AST::HostClass, klass, "Did not autoload class from module init file") assert_equal(name, klass.classname, "Incorrect class was returned") # Now try it with a definition as the base file name = "simpdef" mk_module(name, :define => true, :init => [name]) klass = parser.find_definition("", name) assert_instance_of(AST::Definition, klass, "Did not autoload class from module init file") assert_equal(name, klass.classname, "Incorrect class was returned") # Now try it with namespace classes where both classes are in the init file parser = mkparser modname = "both" name = "sub" mk_module(modname, :init => %w{both both::sub}) # First try it with a namespace klass = parser.find_hostclass("both", name) assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from module init file with a namespace") assert_equal("both::sub", klass.classname, "Incorrect class was returned") # Now try it using the fully qualified name parser = mkparser klass = parser.find_hostclass("", "both::sub") assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from module init file with no namespace") assert_equal("both::sub", klass.classname, "Incorrect class was returned") # Now try it with the class in a different file parser = mkparser modname = "separate" name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) # First try it with a namespace klass = parser.find_hostclass("separate", name) assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from separate file with a namespace") assert_equal("separate::sub", klass.classname, "Incorrect class was returned") # Now try it using the fully qualified name parser = mkparser klass = parser.find_hostclass("", "separate::sub") assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from separate file with no namespace") assert_equal("separate::sub", klass.classname, "Incorrect class was returned") # Now make sure we don't get a failure when there's no module file parser = mkparser modname = "alone" name = "sub" mk_module(modname, :sub => %w{alone::sub}) # First try it with a namespace assert_nothing_raised("Could not autoload file when module file is missing") do klass = parser.find_hostclass("alone", name) end assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from alone file with a namespace") assert_equal("alone::sub", klass.classname, "Incorrect class was returned") # Now try it using the fully qualified name parser = mkparser klass = parser.find_hostclass("", "alone::sub") assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from alone file with no namespace") assert_equal("alone::sub", klass.classname, "Incorrect class was returned") # and with the definition in its own file name = "mymod" mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) klass = parser.find_definition("", "mymod::mydefine") assert_instance_of(AST::Definition, klass, "Did not autoload definition from its own file") assert_equal("mymod::mydefine", klass.classname, "Incorrect definition was returned") end # Make sure class, node, and define methods are case-insensitive def test_structure_case_insensitivity parser = mkparser result = nil assert_nothing_raised do result = parser.newclass "Yayness" end assert_equal(result, parser.find_hostclass("", "yayNess")) assert_nothing_raised do result = parser.newdefine "FunTest" end assert_equal(result, parser.find_definition("", "fUntEst"), "%s was not matched" % "fUntEst") end def test_manifests_with_multiple_environments parser = mkparser :environment => "something" # We use an exception to cut short the processing to simplify our stubbing #Puppet::Module.expects(:find_manifests).with("test", {:cwd => ".", :environment => "something"}).raises(Puppet::ParseError) Puppet::Parser::Files.expects(:find_manifests).with("test", {:cwd => ".", :environment => "something"}).returns([]) assert_raise(Puppet::ImportError) do parser.import("test") end end def test_watch_file_only_once FileTest.stubs(:exists?).returns(true) parser = mkparser parser.watch_file("doh") parser.watch_file("doh") assert_equal(1, parser.files.select { |name, file| file.file == "doh" }.length, "Length of watched 'doh' files was not 1") end end