diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index 9b204cb13..f661fd01b 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -1,496 +1,496 @@ require 'puppet/util' require 'puppet/util/cacher' require 'monitor' require 'puppet/parser/parser_factory' # Just define it, so this class has fewer load dependencies. class Puppet::Node end # Puppet::Node::Environment acts as a container for all configuration # that is expected to vary between environments. # # ## The root environment # # In addition to normal environments that are defined by the user,there is a # special 'root' environment. It is defined as an instance variable on the # Puppet::Node::Environment metaclass. The environment name is `*root*` and can # be accessed by looking up the `:root_environment` using {Puppet.lookup}. # # The primary purpose of the root environment is to contain parser functions # that are not bound to a specific environment. The main case for this is for # logging functions. Logging functions are attached to the 'root' environment # when {Puppet::Parser::Functions.reset} is called. class Puppet::Node::Environment include Puppet::Util::Cacher NO_MANIFEST = :no_manifest # @api private def self.seen @seen ||= {} end # Create a new environment with the given name, or return an existing one # # The environment class memoizes instances so that attempts to instantiate an # environment with the same name with an existing environment will return the # existing environment. # # @overload self.new(environment) # @param environment [Puppet::Node::Environment] # @return [Puppet::Node::Environment] the environment passed as the param, # this is implemented so that a calling class can use strings or # environments interchangeably. # # @overload self.new(string) # @param string [String, Symbol] # @return [Puppet::Node::Environment] An existing environment if it exists, # else a new environment with that name # # @overload self.new() # @return [Puppet::Node::Environment] The environment as set by # Puppet.settings[:environment] # # @api public def self.new(name = nil) return name if name.is_a?(self) 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.create(symbol, split_path(Puppet.settings.value(:modulepath, symbol)), Puppet.settings.value(:manifest, symbol)) seen[symbol] = obj end # Create a new environment with the given name # # @param name [Symbol] the name of the # @param modulepath [Array] the list of paths from which to load modules # @param manifest [String] the path to the manifest for the environment or # the constant Puppet::Node::Environment::NO_MANIFEST if there is none. # @return [Puppet::Node::Environment] # # @api public - def self.create(name, modulepath, manifest) + def self.create(name, modulepath, manifest = NO_MANIFEST) obj = self.allocate obj.send(:initialize, name, expand_dirs(extralibs() + modulepath), manifest == NO_MANIFEST ? manifest : File.expand_path(manifest)) obj end # Instantiate a new environment # # @note {Puppet::Node::Environment.new} is overridden to return memoized # objects, so this will not be invoked with the normal Ruby initialization # semantics. # # @param name [Symbol] The environment name def initialize(name, modulepath, manifest) @name = name @modulepath = modulepath @manifest = manifest end # Creates a new Puppet::Node::Environment instance, overriding any of the passed # parameters. # # @param env_params [Hash<{Symbol => String,Array}>] new environment # parameters (:modulepath, :manifest) # @return [Puppet::Node::Environment] def override_with(env_params) return self.class.create(name, env_params[:modulepath] || modulepath, env_params[:manifest] || manifest) end # Creates a new Puppet::Node::Environment instance, overriding manfiest # and modulepath from the passed settings if they were originally set from # the commandline, or returns self if there is nothing to override. # # @param settings [Puppet::Settings] an initialized puppet settings instance # @return [Puppet::Node::Environment] new overridden environment or self if # there are no commandline changes from settings. def override_from_commandline(settings) overrides = {} overrides[:modulepath] = self.class.split_path(settings[:modulepath]) if settings.set_by_cli?(:modulepath) if settings.set_by_cli?(:manifest) || (settings.set_by_cli?(:manifestdir) && settings[:manifest].start_with?(settings[:manifestdir])) overrides[:manifest] = settings[:manifest] end overrides.empty? ? self : self.override_with(overrides) end # Retrieve the environment for the current process. # # @note This should only used when a catalog is being compiled. # # @api private # # @return [Puppet::Node::Environment] the currently set environment if one # has been explicitly set, else it will return the '*root*' environment def self.current Puppet.deprecation_warning("Puppet::Node::Environment.current has been replaced by Puppet.lookup(:current_environment), see http://links.puppetlabs.com/current-env-deprecation") Puppet.lookup(:current_environment) end # @param [String] name Environment name to check for valid syntax. # @return [Boolean] true if name is valid # @api public def self.valid_name?(name) !!name.match(/\A\w+\Z/) end # Clear all memoized environments and the 'current' environment # # @api private def self.clear seen.clear end # @!attribute [r] name # @api public # @return [Symbol] the human readable environment name that serves as the # environment identifier attr_reader :name # @api public # @return [Array] All directories present on disk in the modulepath def modulepath @modulepath.find_all do |p| Puppet::FileSystem.directory?(p) end end # @api public # @return [Array] All directories in the modulepath (even if they are not present on disk) def full_modulepath @modulepath end # @!attribute [r] manifest # @api public # @return [String] path to the manifest file or directory. attr_reader :manifest # Return an environment-specific Puppet setting. # # @api public # # @param param [String, Symbol] The environment setting to look up # @return [Object] The resolved setting value def [](param) Puppet.settings.value(param, self.name) end # @api public # @return [Puppet::Resource::TypeCollection] The current global TypeCollection def known_resource_types if @known_resource_types.nil? @known_resource_types = Puppet::Resource::TypeCollection.new(self) @known_resource_types.import_ast(perform_initial_import(), '') end @known_resource_types end # Yields each modules' plugin directory if the plugin directory (modulename/lib) # is present on the filesystem. # # @yield [String] Yields the plugin directory from each module to the block. # @api public def each_plugin_directory(&block) modules.map(&:plugin_directory).each do |lib| lib = Puppet::Util::Autoload.cleanpath(lib) yield lib if File.directory?(lib) end end # Locate a module instance by the module name alone. # # @api public # # @param name [String] The module name # @return [Puppet::Module, nil] The module if found, else nil def module(name) modules.find {|mod| mod.name == name} end # Locate a module instance by the full forge name (EG authorname/module) # # @api public # # @param forge_name [String] The module name # @return [Puppet::Module, nil] The module if found, else nil def module_by_forge_name(forge_name) author, modname = forge_name.split('/') found_mod = self.module(modname) found_mod and found_mod.forge_name == forge_name ? found_mod : nil end # @!attribute [r] modules # Return all modules for this environment in the order they appear in the # modulepath. # @note If multiple modules with the same name are present they will # both be added, but methods like {#module} and {#module_by_forge_name} # will return the first matching entry in this list. # @note This value is cached so that the filesystem doesn't have to be # re-enumerated every time this method is invoked, since that # enumeration could be a costly operation and this method is called # frequently. The cache expiry is determined by `Puppet[:filetimeout]`. # @see Puppet::Util::Cacher.cached_attr # @api public # @return [Array] All modules for this environment cached_attr(:modules, Puppet[:filetimeout]) do module_references = [] seen_modules = {} modulepath.each do |path| Dir.entries(path).each do |name| warn_about_mistaken_path(path, name) next if module_references.include?(name) if not seen_modules[name] module_references << {:name => name, :path => File.join(path, name)} seen_modules[name] = true end end end module_references.collect do |reference| begin Puppet::Module.new(reference[:name], reference[:path], self) rescue Puppet::Module::Error nil end end.compact end # Generate a warning if the given directory in a module path entry is named `lib`. # # @api private # # @param path [String] The module directory containing the given directory # @param name [String] The directory name def warn_about_mistaken_path(path, name) if name == "lib" Puppet.debug("Warning: Found directory named 'lib' in module path ('#{path}/lib'); unless " + "you are expecting to load a module named 'lib', your module path may be set " + "incorrectly.") end end # Modules broken out by directory in the modulepath # # @note This method _changes_ the current working directory while enumerating # the modules. This seems rather dangerous. # # @api public # # @return [Hash>] A hash whose keys are file # paths, and whose values is an array of Puppet Modules for that path def modules_by_path modules_by_path = {} modulepath.each do |path| Dir.chdir(path) do module_names = Dir.glob('*').select do |d| FileTest.directory?(d) && (File.basename(d) =~ /\A\w+(-\w+)*\Z/) end modules_by_path[path] = module_names.sort.map do |name| Puppet::Module.new(name, File.join(path, name), self) end end end modules_by_path end # All module requirements for all modules in the environment modulepath # # @api public # # @comment This has nothing to do with an environment. It seems like it was # stuffed into the first convenient class that vaguely involved modules. # # @example # environment.module_requirements # # => { # # 'username/amodule' => [ # # { # # 'name' => 'username/moduledep', # # 'version' => '1.2.3', # # 'version_requirement' => '>= 1.0.0', # # }, # # { # # 'name' => 'username/anotherdep', # # 'version' => '4.5.6', # # 'version_requirement' => '>= 3.0.0', # # } # # ] # # } # # # # @return [Hash>>] See the method example # for an explanation of the return value. def module_requirements deps = {} modules.each do |mod| next unless mod.forge_name deps[mod.forge_name] ||= [] mod.dependencies and mod.dependencies.each do |mod_dep| deps[mod_dep['name']] ||= [] dep_details = { 'name' => mod.forge_name, 'version' => mod.version, 'version_requirement' => mod_dep['version_requirement'] } deps[mod_dep['name']] << dep_details end end deps.each do |mod, mod_deps| deps[mod] = mod_deps.sort_by {|d| d['name']} end deps end # Set a periodic watcher on the file, so we can tell if it has changed. # @param filename [File,String] File instance or filename # @api private def watch_file(file) known_resource_types.watch_file(file.to_s) end def check_for_reparse if (Puppet[:code] != @parsed_code) || (@known_resource_types && @known_resource_types.require_reparse?) @parsed_code = nil @known_resource_types = nil end end # @return [String] The stringified value of the `name` instance variable # @api public def to_s name.to_s end # @return [Symbol] The `name` value, cast to a string, then cast to a symbol. # # @api public # # @note the `name` instance variable is a Symbol, but this casts the value # to a String and then converts it back into a Symbol which will needlessly # create an object that needs to be garbage collected def to_sym to_s.to_sym end # Return only the environment name when serializing. # # The only thing we care about when serializing an environment is its # identity; everything else is ephemeral and should not be stored or # transmitted. # # @api public def to_zaml(z) self.to_s.to_zaml(z) end def self.split_path(path_string) path_string.split(File::PATH_SEPARATOR) end def ==(other) return true if other.kind_of?(Puppet::Node::Environment) && self.name == other.name && self.full_modulepath == other.full_modulepath && self.manifest == other.manifest end def hash [self.class, name, full_modulepath, manifest].hash end private def self.extralibs() if ENV["PUPPETLIB"] split_path(ENV["PUPPETLIB"]) else [] end end def self.expand_dirs(dirs) dirs.collect do |dir| File.expand_path(dir) end end # Reparse the manifests for the given environment # # There are two sources that can be used for the initial parse: # # 1. The value of `Puppet.settings[:code]`: Puppet can take a string from # its settings and parse that as a manifest. This is used by various # Puppet applications to read in a manifest and pass it to the # environment as a side effect. This is attempted first. # 2. The contents of `Puppet.settings[:manifest]`: Puppet will try to load # the environment manifest. By default this is `$manifestdir/site.pp` # # @note This method will return an empty hostclass if # `Puppet.settings[:ignoreimport]` is set to true. # # @return [Puppet::Parser::AST::Hostclass] The AST hostclass object # representing the 'main' hostclass def perform_initial_import return empty_parse_result if Puppet[:ignoreimport] parser = Puppet::Parser::ParserFactory.parser(self) @parsed_code = Puppet[:code] if @parsed_code != "" parser.string = @parsed_code parser.parse else file = self.manifest # if the manifest file is a reference to a directory, parse and combine all .pp files in that # directory if file == NO_MANIFEST - nil + Puppet::Parser::AST::Hostclass.new('') elsif File.directory?(file) parse_results = Dir.entries(file).find_all { |f| f =~ /\.pp$/ }.sort.map do |pp_file| parser.file = File.join(file, pp_file) parser.parse end # Use a parser type specific merger to concatenate the results Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results)) else parser.file = file parser.parse end end rescue => detail @known_resource_types.parse_failed = true msg = "Could not parse for environment #{self}: #{detail}" error = Puppet::Error.new(msg) error.set_backtrace(detail.backtrace) raise error end # Return an empty toplevel hostclass to indicate that no file was loaded # # This is used as the return value of {#perform_initial_import} when # `Puppet.settings[:ignoreimport]` is true. # # @return [Puppet::Parser::AST::Hostclass] def empty_parse_result return Puppet::Parser::AST::Hostclass.new('') end end diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index 9847a14dc..f337d9f25 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -1,108 +1,108 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/files' require 'puppet/application/apply' describe "apply" do include PuppetSpec::Files before :each do Puppet[:reports] = "none" end describe "when applying provided catalogs" do it "can apply catalogs provided in a file in pson" do file_to_create = tmpfile("pson_catalog") catalog = Puppet::Resource::Catalog.new resource = Puppet::Resource.new(:file, file_to_create, :parameters => {:content => "my stuff"}) catalog.add_resource resource manifest = tmpfile("manifest") File.open(manifest, "w") { |f| f.print catalog.to_pson } puppet = Puppet::Application[:apply] puppet.options[:catalog] = manifest puppet.apply expect(Puppet::FileSystem.exist?(file_to_create)).to be_true expect(File.read(file_to_create)).to eq("my stuff") end end it "applies a given file even when a directory environment is specified" do manifest = tmpfile("manifest.pp") File.open(manifest, "w") do |f| f.puts <<-EOF notice('it was applied') EOF end - special = Puppet::Node::Environment.create(:special, [], '') + special = Puppet::Node::Environment.create(:special, []) Puppet.override(:current_environment => special) do Puppet[:environment] = 'special' puppet = Puppet::Application[:apply] puppet.stubs(:command_line).returns(stub('command_line', :args => [manifest])) expect { puppet.run_command }.to exit_with(0) end expect(@logs.map(&:to_s)).to include('it was applied') end context "with a module" do let(:modulepath) { tmpdir('modulepath') } let(:execute) { 'include amod' } let(:args) { ['-e', execute, '--modulepath', modulepath] } before(:each) do Puppet::FileSystem.mkpath("#{modulepath}/amod/manifests") File.open("#{modulepath}/amod/manifests/init.pp", "w") do |f| f.puts <<-EOF class amod{ notice('amod class included') } EOF end create_default_directory_environment end def create_default_directory_environment Puppet::FileSystem.mkpath("#{Puppet[:environmentpath]}/#{Puppet[:environment]}") end def init_cli_args_and_apply_app(args, execute) Puppet.initialize_settings(args) puppet = Puppet::Application.find(:apply).new(stub('command_line', :subcommand_name => :apply, :args => args)) puppet.options[:code] = execute return puppet end it "looks in --modulepath even when the default directory environment exists" do apply = init_cli_args_and_apply_app(args, execute) expect do expect { apply.run }.to exit_with(0) end.to have_printed('amod class included') end it "looks in --modulepath even when given a specific directory --environment" do args << '--environment' << 'production' apply = init_cli_args_and_apply_app(args, execute) expect do expect { apply.run }.to exit_with(0) end.to have_printed('amod class included') end it "looks in --modulepath when given multiple paths in --modulepath" do args = ['-e', execute, '--modulepath', [tmpdir('notmodulepath'), modulepath].join(File::PATH_SEPARATOR)] apply = init_cli_args_and_apply_app(args, execute) expect do expect { apply.run }.to exit_with(0) end.to have_printed('amod class included') end end end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index d35d60b44..103a028a2 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -1,89 +1,89 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/file_content/file_server' require 'shared_behaviours/file_server_terminus' require 'puppet_spec/files' describe Puppet::Indirector::FileContent::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" include PuppetSpec::Files before do @terminus = Puppet::Indirector::FileContent::FileServer.new @test_class = Puppet::FileServing::Content Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) end it "should find plugin file content in the environment specified in the request" do path = tmpfile("file_content_with_env") Dir.mkdir(path) modpath = File.join(path, "mod") FileUtils.mkdir_p(File.join(modpath, "lib")) file = File.join(modpath, "lib", "file.rb") File.open(file, "wb") { |f| f.write "1\r\n" } Puppet.settings[:modulepath] = "/no/such/file" - env = Puppet::Node::Environment.create(:foo, [path], '') + env = Puppet::Node::Environment.create(:foo, [path]) result = Puppet::FileServing::Content.indirection.search("plugins", :environment => env, :recurse => true) result.should_not be_nil result.length.should == 2 result.map {|x| x.should be_instance_of(Puppet::FileServing::Content) } result.find {|x| x.relative_path == 'file.rb' }.content.should == "1\r\n" end it "should find file content in modules" do path = tmpfile("file_content") Dir.mkdir(path) modpath = File.join(path, "mymod") FileUtils.mkdir_p(File.join(modpath, "files")) file = File.join(modpath, "files", "myfile") File.open(file, "wb") { |f| f.write "1\r\n" } Puppet.settings[:modulepath] = path result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\r\n" end it "should find file content in files when node name expansions are used" do Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileSystem.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) @path = tmpfile("file_server_testing") Dir.mkdir(@path) subdir = File.join(@path, "mynode") Dir.mkdir(subdir) File.open(File.join(subdir, "myfile"), "wb") { |f| f.write "1\r\n" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.stubs(:allowed?).returns true @mount1.path = File.join(@path, "%h") @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) path = File.join(@path, "myfile") result = Puppet::FileServing::Content.indirection.find("one/myfile", :environment => "foo", :node => "mynode") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\r\n" end end diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index d193367ee..1f428ac3f 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -1,109 +1,109 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/files' require 'puppet_spec/scope' require 'matchers/resource' describe Puppet::Node::Environment do include PuppetSpec::Files include Matchers::Resource def a_module_in(name, dir) Dir.mkdir(dir) moddir = File.join(dir, name) Dir.mkdir(moddir) moddir end it "should be able to return each module from its environment with the environment, name, and path set correctly" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir mods["mod#{num}"] = a_module_in("mod#{num}", dir) end - environment = Puppet::Node::Environment.create(:foo, dirs, '') + environment = Puppet::Node::Environment.create(:foo, dirs) environment.modules.each do |mod| mod.environment.should == environment mod.path.should == mods[mod.name] end end it "should not yield the same module from different module paths" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir a_module_in("mod", dir) end - environment = Puppet::Node::Environment.create(:foo, dirs, '') + environment = Puppet::Node::Environment.create(:foo, dirs) mods = environment.modules mods.length.should == 1 mods[0].path.should == File.join(base, "dir1", "mod") end shared_examples_for "the environment's initial import" do |settings| it "a manifest referring to a directory invokes parsing of all its files in sorted order" do settings.each do |name, value| Puppet[name] = value end # fixture has three files 00_a.pp, 01_b.pp, and 02_c.pp. The 'b' file # depends on 'a' being evaluated first. The 'c' file is empty (to ensure # empty things do not break the directory import). # dirname = my_fixture('sitedir') # Set the manifest to the directory to make it parse and combine them when compiling node = Puppet::Node.new('testnode', :environment => Puppet::Node::Environment.create(:testing, [], dirname)) catalog = Puppet::Parser::Compiler.compile(node) expect(catalog).to have_resource('Class[a]') expect(catalog).to have_resource('Class[b]') expect(catalog).to have_resource('Notify[variables]').with_parameter(:message, "a: 10, b: 10") end end describe 'using classic parser' do it_behaves_like "the environment's initial import", :parser => 'current', # fixture uses variables that are set in a particular order (this ensures # that files are parsed and combined in the right order or an error will # be raised if 'b' is evaluated before 'a'). :strict_variables => true end describe 'using future parser' do it_behaves_like "the environment's initial import", :parser => 'future', :evaluator => 'future', # Turned off because currently future parser turns on the binder which # causes lookup of facts that are uninitialized and it will fail with # errors for 'osfamily' etc. This can be turned back on when the binder # is taken out of the equation. :strict_variables => false context 'and evaluator current' do it_behaves_like "the environment's initial import", :parser => 'future', :evaluator => 'current', :strict_variables => false end end end diff --git a/spec/integration/resource/type_collection_spec.rb b/spec/integration/resource/type_collection_spec.rb index 6349460be..57dc04711 100755 --- a/spec/integration/resource/type_collection_spec.rb +++ b/spec/integration/resource/type_collection_spec.rb @@ -1,94 +1,94 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/files' require 'puppet/resource/type_collection' describe Puppet::Resource::TypeCollection do describe "when autoloading from modules" do include PuppetSpec::Files before do @dir = tmpfile("autoload_testing") FileUtils.mkdir_p @dir - environment = Puppet::Node::Environment.create(:env, [@dir], '') + environment = Puppet::Node::Environment.create(:env, [@dir]) @code = environment.known_resource_types end # Setup a module. def mk_module(name, files = {}) mdir = File.join(@dir, name) mandir = File.join(mdir, "manifests") FileUtils.mkdir_p mandir defs = files.delete(:define) Dir.chdir(mandir) do files.each do |file, classes| File.open("#{file}.pp", "w") do |f| classes.each { |klass| if defs f.puts "define #{klass} {}" else f.puts "class #{klass} {}" end } end end end end it "should return nil when a class can't be found or loaded" do @code.find_hostclass('', 'nosuchclass').should be_nil end it "should load the module's init file first" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("", name).name.should == name end it "should load the module's init file even when searching from a different namespace" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("other::ns", name).name.should == name end it "should be able to load definitions from the module base file" do name = "simpdef" mk_module(name, :define => true, :init => [name]) @code.find_definition("", name).name.should == name end it "should be able to load qualified classes from the module base file" do modname = "both" name = "sub" mk_module(modname, :init => %w{both both::sub}) @code.find_hostclass("both", name).name.should == "both::sub" end it "should be able load classes from a separate file" do modname = "separate" name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) @code.find_hostclass("separate", name).name.should == "separate::sub" end it "should not fail when loading from a separate file if there is no module file" do modname = "alone" name = "sub" mk_module(modname, :sub => %w{alone::sub}) lambda { @code.find_hostclass("alone", name) }.should_not raise_error end it "should be able to load definitions from their own file" do name = "mymod" mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) @code.find_definition("", "mymod::mydefine").name.should == "mymod::mydefine" end end end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 69cd284b4..bd6af5322 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -1,455 +1,455 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/application/apply' require 'puppet/file_bucket/dipper' require 'puppet/configurer' require 'fileutils' describe Puppet::Application::Apply do before :each do @apply = Puppet::Application[:apply] Puppet::Util::Log.stubs(:newdestination) Puppet[:reports] = "none" end after :each do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.cache_class = nil Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil end [:debug,:loadclasses,:test,:verbose,:use_nodes,:detailed_exitcodes,:catalog, :write_catalog_summary].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @apply.options.expects(:[]=).with(option, 'arg') @apply.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @apply.options.expects(:[]=).with(:code, 'arg') @apply.send("handle_execute".to_sym, 'arg') end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @apply.handle_logdest("console") end it "should set the setdest options to true" do @apply.options.expects(:[]=).with(:setdest,true) @apply.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) Puppet::Transaction::Report.indirection.stubs(:cache_class=) end describe "with --test" do it "should call setup_test" do @apply.options[:test] = true @apply.expects(:setup_test) @apply.setup end it "should set options[:verbose] to true" do @apply.setup_test @apply.options[:verbose].should == true end it "should set options[:show_diff] to true" do Puppet.settings.override_default(:show_diff, false) @apply.setup_test Puppet[:show_diff].should == true end it "should set options[:detailed_exitcodes] to true" do @apply.setup_test @apply.options[:detailed_exitcodes].should == true end end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @apply.setup end it "should set INT trap" do Signal.expects(:trap).with(:INT) @apply.setup end it "should set log level to debug if --debug was passed" do @apply.options[:debug] = true @apply.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @apply.options[:verbose] = true @apply.setup Puppet::Log.level.should == :info end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns true Puppet.settings.expects(:print_configs).returns true expect { @apply.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @apply.setup }.to exit_with 1 end it "should tell the report handler to cache locally as yaml" do Puppet::Transaction::Report.indirection.expects(:cache_class=).with(:yaml) @apply.setup end it "configures a profiler when profiling is enabled" do Puppet[:profile] = true @apply.setup expect(Puppet::Util::Profiler.current).to be_a(Puppet::Util::Profiler::WallClock) end it "does not have a profiler if profiling is disabled" do Puppet[:profile] = false @apply.setup expect(Puppet::Util::Profiler.current).to eq(Puppet::Util::Profiler::NONE) end it "should set default_file_terminus to `file_server` to be local" do @apply.app_defaults[:default_file_terminus].should == :file_server end end describe "when executing" do it "should dispatch to 'apply' if it was called with 'apply'" do @apply.options[:catalog] = "foo" @apply.expects(:apply) @apply.run_command end it "should dispatch to main otherwise" do @apply.stubs(:options).returns({}) @apply.expects(:main) @apply.run_command end describe "the main command" do include PuppetSpec::Files before :each do Puppet[:prerun_command] = '' Puppet[:postrun_command] = '' Puppet::Node::Facts.indirection.terminus_class = :memory Puppet::Node::Facts.indirection.cache_class = :memory Puppet::Node.indirection.terminus_class = :memory Puppet::Node.indirection.cache_class = :memory @facts = Puppet::Node::Facts.new(Puppet[:node_name_value]) Puppet::Node::Facts.indirection.save(@facts) @node = Puppet::Node.new(Puppet[:node_name_value]) Puppet::Node.indirection.save(@node) @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.indirection.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub('transaction') @catalog.stubs(:apply).returns(@transaction) Puppet::Util::Storage.stubs(:load) Puppet::Configurer.any_instance.stubs(:save_last_run_summary) # to prevent it from trying to write files end after :each do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.cache_class = nil end around :each do |example| Puppet.override(:current_environment => - Puppet::Node::Environment.create(:production, [], '')) do + Puppet::Node::Environment.create(:production, [])) do example.run end end it "should set the code to run from --code" do @apply.options[:code] = "code to run" Puppet.expects(:[]=).with(:code,"code to run") expect { @apply.main }.to exit_with 0 end it "should set the code to run from STDIN if no arguments" do @apply.command_line.stubs(:args).returns([]) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") expect { @apply.main }.to exit_with 0 end it "should raise an error if a file is passed on command line and the file does not exist" do noexist = tmpfile('noexist.pp') @apply.command_line.stubs(:args).returns([noexist]) lambda { @apply.main }.should raise_error(RuntimeError, "Could not find file #{noexist}") end it "should set the manifest to the first file and warn other files will be skipped" do manifest = tmpfile('starwarsIV') FileUtils.touch(manifest) @apply.command_line.stubs(:args).returns([manifest, 'starwarsI', 'starwarsII']) expect { @apply.main }.to exit_with 0 msg = @logs.find {|m| m.message =~ /Only one file can be applied per run/ } msg.message.should == 'Only one file can be applied per run. Skipping starwarsI, starwarsII' msg.level.should == :warning end it "should raise an error if we can't find the node" do Puppet::Node.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error(RuntimeError, /Could not find node/) end it "should load custom classes if loadclasses" do @apply.options[:loadclasses] = true classfile = tmpfile('classfile') File.open(classfile, 'w') { |c| c.puts 'class' } Puppet[:classfile] = classfile @node.expects(:classes=).with(['class']) expect { @apply.main }.to exit_with 0 end it "should compile the catalog" do Puppet::Resource::Catalog.indirection.expects(:find).returns(@catalog) expect { @apply.main }.to exit_with 0 end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) expect { @apply.main }.to exit_with 0 end it "should finalize the catalog" do @catalog.expects(:finalize) expect { @apply.main }.to exit_with 0 end it "should not save the classes or resource file by default" do @catalog.expects(:write_class_file).never @catalog.expects(:write_resource_file).never expect { @apply.main }.to exit_with 0 end it "should save the classes and resources files when requested" do @apply.options[:write_catalog_summary] = true @catalog.expects(:write_class_file).once @catalog.expects(:write_resource_file).once expect { @apply.main }.to exit_with 0 end it "should call the prerun and postrun commands on a Configurer instance" do Puppet::Configurer.any_instance.expects(:execute_prerun_command).returns(true) Puppet::Configurer.any_instance.expects(:execute_postrun_command).returns(true) expect { @apply.main }.to exit_with 0 end it "should apply the catalog" do @catalog.expects(:apply).returns(stub_everything('transaction')) expect { @apply.main }.to exit_with 0 end it "should save the last run summary" do Puppet[:noop] = false report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns(report) Puppet::Configurer.any_instance.expects(:save_last_run_summary).with(report) expect { @apply.main }.to exit_with 0 end describe "when using node_name_fact" do before :each do @facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name') Puppet::Node::Facts.indirection.save(@facts) @node = Puppet::Node.new('other_node_name') Puppet::Node.indirection.save(@node) Puppet[:node_name_fact] = 'my_name_fact' end it "should set the facts name based on the node_name_fact" do expect { @apply.main }.to exit_with 0 @facts.name.should == 'other_node_name' end it "should set the node_name_value based on the node_name_fact" do expect { @apply.main }.to exit_with 0 Puppet[:node_name_value].should == 'other_node_name' end it "should merge in our node the loaded facts" do @facts.values.merge!('key' => 'value') expect { @apply.main }.to exit_with 0 @node.parameters['key'].should == 'value' end it "should raise an error if we can't find the facts" do Puppet::Node::Facts.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end end describe "with detailed_exitcodes" do before :each do @apply.options[:detailed_exitcodes] = true end it "should exit with report's computed exit status" do Puppet[:noop] = false Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) expect { @apply.main }.to exit_with 666 end it "should exit with report's computed exit status, even if --noop is set" do Puppet[:noop] = true Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) expect { @apply.main }.to exit_with 666 end it "should always exit with 0 if option is disabled" do Puppet[:noop] = false report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) expect { @apply.main }.to exit_with 0 end it "should always exit with 0 if --noop" do Puppet[:noop] = true report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) expect { @apply.main }.to exit_with 0 end end end describe "the 'apply' command" do # We want this memoized, and to be able to adjust the content, so we # have to do it ourselves. def temporary_catalog(content = '"something"') @tempfile = Tempfile.new('catalog.pson') @tempfile.write(content) @tempfile.close @tempfile.path end it "should read the catalog in from disk if a file name is provided" do @apply.options[:catalog] = temporary_catalog Puppet::Resource::Catalog.stubs(:convert_from). with(:pson,'"something"').returns(Puppet::Resource::Catalog.new) @apply.apply end it "should read the catalog in from stdin if '-' is provided" do @apply.options[:catalog] = "-" $stdin.expects(:read).returns '"something"' Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'"something"').returns Puppet::Resource::Catalog.new @apply.apply end it "should deserialize the catalog from the default format" do @apply.options[:catalog] = temporary_catalog Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'"something"').returns Puppet::Resource::Catalog.new @apply.apply end it "should fail helpfully if deserializing fails" do @apply.options[:catalog] = temporary_catalog('something syntactically invalid') lambda { @apply.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @apply.options[:catalog] = temporary_catalog 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) @apply.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @apply.options[:catalog] = temporary_catalog 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", :pluginsync => false) @apply.apply end end end describe "apply_catalog" do it "should call the configurer with the catalog" do catalog = "I am a catalog" Puppet::Configurer.any_instance.expects(:run). with(:catalog => catalog, :pluginsync => false) @apply.send(:apply_catalog, catalog) end end end diff --git a/spec/unit/face/module/install_spec.rb b/spec/unit/face/module/install_spec.rb index 868b22511..3e3719786 100644 --- a/spec/unit/face/module/install_spec.rb +++ b/spec/unit/face/module/install_spec.rb @@ -1,113 +1,113 @@ require 'spec_helper' require 'puppet/face' require 'puppet/module_tool' describe "puppet module install" do include PuppetSpec::Files subject { Puppet::Face[:module, :current] } describe "option validation" do let(:sep) { File::PATH_SEPARATOR } let(:fakefirstpath) { make_absolute("/my/fake/modpath") } let(:fakesecondpath) { make_absolute("/other/fake/path") } let(:fakemodpath) { "#{fakefirstpath}#{sep}#{fakesecondpath}" } let(:fakedirpath) { make_absolute("/my/fake/path") } let(:options) { {} } let(:environment) do - Puppet::Node::Environment.create(:env, [fakefirstpath, fakesecondpath], '') + Puppet::Node::Environment.create(:env, [fakefirstpath, fakesecondpath]) end let(:expected_options) do { :target_dir => fakefirstpath, :environment_instance => environment, } end around(:each) do |example| Puppet.override(:current_environment => environment) do example.run end end context "without any options" do it "requires a name" do pattern = /wrong number of arguments/ expect { subject.install }.to raise_error ArgumentError, pattern end it "does not require any options" do expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache") end end it "accepts the --force option" do options[:force] = true expected_options.merge!(options) expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache", options) end it "accepts the --target-dir option" do options[:target_dir] = make_absolute("/foo/puppet/modules") expected_options.merge!(options) expected_options[:environment_instance] = environment.override_with(:modulepath => [options[:target_dir], fakefirstpath, fakesecondpath]) expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache", options) end it "accepts the --version option" do options[:version] = "0.0.1" expected_options.merge!(options) expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache", options) end it "accepts the --ignore-dependencies option" do options[:ignore_dependencies] = true expected_options.merge!(options) expects_installer_run_with("puppetlabs-apache", expected_options) subject.install("puppetlabs-apache", options) end end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :install } its(:summary) { should =~ /install.*module/im } its(:description) { should =~ /install.*module/im } its(:returns) { should =~ /pathname/i } its(:examples) { should_not be_empty } %w{ license copyright summary description returns examples }.each do |doc| context "of the" do its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } end end end def expects_installer_run_with(name, options) installer = mock("Installer") install_dir = mock("InstallDir") forge = mock("Forge") Puppet::Forge.expects(:new).with("PMT", subject.version).returns(forge) Puppet::ModuleTool::InstallDirectory.expects(:new). with(Pathname.new(expected_options[:target_dir])). returns(install_dir) Puppet::ModuleTool::Applications::Installer.expects(:new). with("puppetlabs-apache", forge, install_dir, expected_options). returns(installer) installer.expects(:run) end end diff --git a/spec/unit/face/module/list_spec.rb b/spec/unit/face/module/list_spec.rb index 9fc4e7121..314d4d762 100644 --- a/spec/unit/face/module/list_spec.rb +++ b/spec/unit/face/module/list_spec.rb @@ -1,195 +1,195 @@ # encoding: UTF-8 require 'spec_helper' require 'puppet/face' require 'puppet/module_tool' require 'puppet_spec/modules' describe "puppet module list" do include PuppetSpec::Files before do dir = tmpdir("deep_path") @modpath1 = File.join(dir, "modpath1") @modpath2 = File.join(dir, "modpath2") @modulepath = "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}" Puppet.settings[:modulepath] = @modulepath FileUtils.mkdir_p(@modpath1) FileUtils.mkdir_p(@modpath2) end around do |example| Puppet.override(:environments => Puppet::Environments::Legacy.new()) do example.run end end it "should return an empty list per dir in path if there are no modules" do Puppet.settings[:modulepath] = @modulepath Puppet::Face[:module, :current].list.should == { @modpath1 => [], @modpath2 => [] } end it "should include modules separated by the environment's modulepath" do foomod1 = PuppetSpec::Modules.create('foo', @modpath1) barmod1 = PuppetSpec::Modules.create('bar', @modpath1) foomod2 = PuppetSpec::Modules.create('foo', @modpath2) - usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2], '') + usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2]) Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do Puppet::Face[:module, :current].list(:environment => 'useme').should == { @modpath1 => [ Puppet::Module.new('bar', barmod1.path, usedenv), Puppet::Module.new('foo', foomod1.path, usedenv) ], @modpath2 => [Puppet::Module.new('foo', foomod2.path, usedenv)] } end end it "should use the specified environment" do foomod = PuppetSpec::Modules.create('foo', @modpath1) barmod = PuppetSpec::Modules.create('bar', @modpath1) - usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2], '') + usedenv = Puppet::Node::Environment.create(:useme, [@modpath1, @modpath2]) Puppet.override(:environments => Puppet::Environments::Static.new(usedenv)) do Puppet::Face[:module, :current].list(:environment => 'useme').should == { @modpath1 => [ Puppet::Module.new('bar', barmod.path, usedenv), Puppet::Module.new('foo', foomod.path, usedenv) ], @modpath2 => [] } end end it "should use the specified modulepath" do foomod = PuppetSpec::Modules.create('foo', @modpath1) barmod = PuppetSpec::Modules.create('bar', @modpath2) modules = Puppet::Face[:module, :current].list(:modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") expect(modules[@modpath1].first.name).to eq('foo') expect(modules[@modpath1].first.path).to eq(foomod.path) expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2]) expect(modules[@modpath2].first.name).to eq('bar') expect(modules[@modpath2].first.path).to eq(barmod.path) expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2]) end it "prefers a given modulepath over the modulepath from the given environment" do foomod = PuppetSpec::Modules.create('foo', @modpath1) barmod = PuppetSpec::Modules.create('bar', @modpath2) - env = Puppet::Node::Environment.create(:myenv, ['/tmp/notused'], '') + env = Puppet::Node::Environment.create(:myenv, ['/tmp/notused']) Puppet[:modulepath] = "" modules = Puppet::Face[:module, :current].list(:environment => 'myenv', :modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") expect(modules[@modpath1].first.name).to eq('foo') expect(modules[@modpath1].first.path).to eq(foomod.path) expect(modules[@modpath1].first.environment.modulepath).to eq([@modpath1, @modpath2]) expect(modules[@modpath1].first.environment.name).to_not eq(:myenv) expect(modules[@modpath2].first.name).to eq('bar') expect(modules[@modpath2].first.path).to eq(barmod.path) expect(modules[@modpath2].first.environment.modulepath).to eq([@modpath1, @modpath2]) expect(modules[@modpath2].first.environment.name).to_not eq(:myenv) end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :list } its(:summary) { should =~ /list.*module/im } its(:description) { should =~ /list.*module/im } its(:returns) { should =~ /hash of paths to module objects/i } its(:examples) { should_not be_empty } end describe "when rendering" do it "should explicitly state when a modulepath is empty" do empty_modpath = tmpdir('empty') Puppet::Face[:module, :current].list_when_rendering_console( { empty_modpath => [] }, {:modulepath => empty_modpath} ).should == <<-HEREDOC.gsub(' ', '') #{empty_modpath} (no modules installed) HEREDOC end it "should print both modules with and without metadata" do modpath = tmpdir('modpath') Puppet.settings[:modulepath] = modpath PuppetSpec::Modules.create('nometadata', modpath) PuppetSpec::Modules.create('metadata', modpath, :metadata => {:author => 'metaman'}) dependency_tree = Puppet::Face[:module, :current].list output = Puppet::Face[:module, :current]. list_when_rendering_console(dependency_tree, {}) output.should == <<-HEREDOC.gsub(' ', '') #{modpath} ├── metaman-metadata (\e[0;36mv9.9.9\e[0m) └── nometadata (\e[0;36m???\e[0m) HEREDOC end it "should print the modulepaths in the order they are in the modulepath setting" do path1 = tmpdir('b') path2 = tmpdir('c') path3 = tmpdir('a') sep = File::PATH_SEPARATOR Puppet.settings[:modulepath] = "#{path1}#{sep}#{path2}#{sep}#{path3}" Puppet::Face[:module, :current].list_when_rendering_console( { path2 => [], path3 => [], path1 => [], }, {} ).should == <<-HEREDOC.gsub(' ', '') #{path1} (no modules installed) #{path2} (no modules installed) #{path3} (no modules installed) HEREDOC end it "should print dependencies as a tree" do PuppetSpec::Modules.create('dependable', @modpath1, :metadata => { :version => '0.0.5'}) PuppetSpec::Modules.create( 'other_mod', @modpath1, :metadata => { :version => '1.0.0', :dependencies => [{ "version_requirement" => ">= 0.0.5", "name" => "puppetlabs/dependable" }] } ) dependency_tree = Puppet::Face[:module, :current].list output = Puppet::Face[:module, :current].list_when_rendering_console( dependency_tree, {:tree => true} ) output.should == <<-HEREDOC.gsub(' ', '') #{@modpath1} └─┬ puppetlabs-other_mod (\e[0;36mv1.0.0\e[0m) └── puppetlabs-dependable (\e[0;36mv0.0.5\e[0m) #{@modpath2} (no modules installed) HEREDOC end end end diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb index 36209dc96..d046aa32c 100644 --- a/spec/unit/face/module/uninstall_spec.rb +++ b/spec/unit/face/module/uninstall_spec.rb @@ -1,70 +1,70 @@ require 'spec_helper' require 'puppet/face' require 'puppet/module_tool' describe "puppet module uninstall" do subject { Puppet::Face[:module, :current] } let(:options) do {} end let(:modulepath) { File.expand_path('/module/path') } let(:environment) do - Puppet::Node::Environment.create(:env, [modulepath], '') + Puppet::Node::Environment.create(:env, [modulepath]) end let(:expected_options) do { :target_dir => modulepath, :environment_instance => environment, } end describe "option validation" do around(:each) do |example| Puppet.override(:current_environment => environment) do example.run end end context "without any options" do it "should require a name" do pattern = /wrong number of arguments/ expect { subject.uninstall }.to raise_error ArgumentError, pattern end it "should not require any options" do Puppet::ModuleTool::Applications::Uninstaller.expects(:run).once subject.uninstall("puppetlabs-apache") end end it "should accept the --version option" do options[:version] = "1.0.0" expected_options.merge!(:version => '1.0.0') Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once subject.uninstall("puppetlabs-apache", options) end it "should accept the --force flag" do options[:force] = true expected_options.merge!(:force => true) Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", has_entries(expected_options)).once subject.uninstall("puppetlabs-apache", options) end end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :uninstall } its(:summary) { should =~ /uninstall.*module/im } its(:description) { should =~ /uninstall.*module/im } its(:returns) { should =~ /hash of module objects.*/im } its(:examples) { should_not be_empty } %w{ license copyright summary description returns examples }.each do |doc| context "of the" do its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } end end end end diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb index 3bf24bcb0..89e1b049a 100644 --- a/spec/unit/face/parser_spec.rb +++ b/spec/unit/face/parser_spec.rb @@ -1,70 +1,70 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/face' describe Puppet::Face[:parser, :current] do include PuppetSpec::Files let(:parser) { Puppet::Face[:parser, :current] } context "from an interactive terminal" do before :each do from_an_interactive_terminal end it "validates the configured site manifest when no files are given" do Puppet[:manifest] = file_containing('site.pp', "{ invalid =>") expect { parser.validate() }.to exit_with(1) end it "validates the given file" do manifest = file_containing('site.pp', "{ invalid =>") expect { parser.validate(manifest) }.to exit_with(1) end it "runs error free when there are no validation errors" do manifest = file_containing('site.pp', "notify { valid: }") parser.validate(manifest) end it "reports missing files" do expect do parser.validate("missing.pp") end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m) end it "parses supplied manifest files in the context of a directory environment" do manifest = file_containing('test.pp', "{ invalid =>") env_loader = Puppet::Environments::Static.new( - Puppet::Node::Environment.create(:special, [], '') + Puppet::Node::Environment.create(:special, []) ) Puppet.override(:environments => env_loader) do Puppet[:environment] = 'special' expect { parser.validate(manifest) }.to exit_with(1) end expect(@logs.join).to match(/environment special.*Syntax error at '\{'/) end end it "validates the contents of STDIN when no files given and STDIN is not a tty" do from_a_piped_input_of("{ invalid =>") expect { parser.validate() }.to exit_with(1) end def from_an_interactive_terminal STDIN.stubs(:tty?).returns(true) end def from_a_piped_input_of(contents) STDIN.stubs(:tty?).returns(false) STDIN.stubs(:read).returns(contents) end end diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb index ac3c98bc4..7d4ce5a9e 100755 --- a/spec/unit/indirector/node/active_record_spec.rb +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -1,48 +1,48 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/node' describe "Puppet::Node::ActiveRecord", :if => Puppet.features.rails? && Puppet.features.sqlite? do include PuppetSpec::Files let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } - let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } + let(:environment) { Puppet::Node::Environment.create(:myenv, []) } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::ActiveRecord.new } before do require 'puppet/indirector/node/active_record' end it "should issue a deprecation warning" do Puppet.expects(:deprecation_warning).with() { |msg| msg =~ /ActiveRecord-based storeconfigs and inventory are deprecated/ } Puppet[:statedir] = tmpdir('active_record_tmp') Puppet[:railslog] = '$statedir/rails.log' node_indirection end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end it "should call fact_merge when a node is found" do db_instance = stub 'db_instance' Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance node = Puppet::Node.new(nodename) db_instance.expects(:to_puppet).returns node Puppet[:statedir] = tmpdir('active_record_tmp') Puppet[:railslog] = '$statedir/rails.log' Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) node_indirection.find(request).parameters.should include(fact_values) end end diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb index c19a30150..76860e15d 100755 --- a/spec/unit/indirector/node/ldap_spec.rb +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -1,441 +1,441 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do let(:nodename) { "mynode.domain.com" } let(:node_indirection) { Puppet::Node::Ldap.new } - let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } + let(:environment) { Puppet::Node::Environment.create(:myenv, []) } let(:fact_values) { {:afact => "a value", "one" => "boo"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } before do Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => environment).returns(facts) end describe "when searching for a single node" do let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } it "should convert the hostname into a search filter" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry node_indirection.name2hash(nodename) end it "should convert any found entry into a hash" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry myhash = {"myhash" => true} node_indirection.expects(:entry2hash).with(entry).returns myhash node_indirection.name2hash(nodename).should == myhash end # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. describe "when an ldap entry is found" do before do @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.stubs(:ldapsearch).yields @entry end it "should convert the entry to a hash" do node_indirection.entry2hash(@entry).should be_instance_of(Hash) end it "should add the entry's common name to the hash if fqdn if false" do node_indirection.entry2hash(@entry,fqdn = false)[:name].should == "mynode" end it "should add the entry's fqdn name to the hash if fqdn if true" do node_indirection.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" end it "should add all of the entry's classes to the hash" do @entry.stubs(:vals).with("puppetclass").returns %w{one two} node_indirection.entry2hash(@entry)[:classes].should == %w{one two} end it "should deduplicate class values" do @entry.stubs(:to_hash).returns({}) node_indirection.stubs(:class_attributes).returns(%w{one two}) @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{b c}) node_indirection.entry2hash(@entry)[:classes].should == %w{a b c} end it "should add the entry's environment to the hash" do @entry.stubs(:to_hash).returns("environment" => %w{production}) node_indirection.entry2hash(@entry)[:environment].should == "production" end it "should add all stacked parameters as parameters in the hash" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) result = node_indirection.entry2hash(@entry) result[:parameters]["one"].should == "two" result[:parameters]["three"].should == "four" end it "should not add the stacked parameter as a normal parameter" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) node_indirection.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil end it "should add all other attributes as parameters in the hash" do @entry.stubs(:to_hash).returns("foo" => %w{one two}) node_indirection.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} end it "should return single-value parameters as strings, not arrays" do @entry.stubs(:to_hash).returns("foo" => %w{one}) node_indirection.entry2hash(@entry)[:parameters]["foo"].should == "one" end it "should convert 'true' values to the boolean 'true'" do @entry.stubs(:to_hash).returns({"one" => ["true"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == true end it "should convert 'false' values to the boolean 'false'" do @entry.stubs(:to_hash).returns({"one" => ["false"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == false end it "should convert 'true' values to the boolean 'true' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] end it "should convert 'false' values to the boolean 'false' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] end it "should add the parent's name if present" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) node_indirection.entry2hash(@entry)[:parent].should == "foo" end it "should fail if more than one parent is specified" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) node_indirection.entry2hash(@entry)[:parent].should == "foo" end end it "should search first for the provided key" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns({}) node_indirection.find(request) end it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) node_indirection.expects(:name2hash).with("mynode").returns({}) node_indirection.find(request) end it "should search for default information if no information can be found for the key" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) node_indirection.expects(:name2hash).with("mynode").returns(nil) node_indirection.expects(:name2hash).with("default").returns({}) node_indirection.find(request) end it "should return nil if no results are found in ldap" do node_indirection.stubs(:name2hash).returns nil node_indirection.find(request).should be_nil end it "should return a node object if results are found in ldap" do node_indirection.stubs(:name2hash).returns({}) node_indirection.find(request).should be end describe "and node information is found in LDAP" do before do @result = {} node_indirection.stubs(:name2hash).returns @result end it "should create the node with the correct name, even if it was found by a different name" do node_indirection.expects(:name2hash).with(nodename).returns nil node_indirection.expects(:name2hash).with("mynode").returns @result node_indirection.find(request).name.should == nodename end it "should add any classes from ldap" do classes = %w{a b c d} @result[:classes] = classes node_indirection.find(request).classes.should == classes end it "should add all entry attributes as node parameters" do params = {"one" => "two", "three" => "four"} @result[:parameters] = params node_indirection.find(request).parameters.should include(params) end it "should set the node's environment to the environment of the results" do - result_env = Puppet::Node::Environment.create(:local_test, [], '') + result_env = Puppet::Node::Environment.create(:local_test, []) Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => result_env).returns(facts) @result[:environment] = "local_test" Puppet.override(:environments => Puppet::Environments::Static.new(result_env)) do node_indirection.find(request).environment.should == result_env end end it "should retain false parameter values" do @result[:parameters] = {} @result[:parameters]["one"] = false node_indirection.find(request).parameters.should include({"one" => false}) end it "should merge the node's facts after the parameters from ldap are assigned" do # Make sure we've got data to start with, so the parameters are actually set. params = {"one" => "yay", "two" => "hooray"} @result[:parameters] = params # Node implements its own merge so that an existing param takes # precedence over facts. We get the same result here by merging params # into facts node_indirection.find(request).parameters.should == facts.values.merge(params) end describe "and a parent node is specified" do before do @entry = {:classes => [], :parameters => {}} @parent = {:classes => [], :parameters => {}} @parent_parent = {:classes => [], :parameters => {}} node_indirection.stubs(:name2hash).with(nodename).returns(@entry) node_indirection.stubs(:name2hash).with('parent').returns(@parent) node_indirection.stubs(:name2hash).with('parent_parent').returns(@parent_parent) node_indirection.stubs(:parent_attribute).returns(:parent) end it "should search for the parent node" do @entry[:parent] = "parent" node_indirection.expects(:name2hash).with(nodename).returns @entry node_indirection.expects(:name2hash).with('parent').returns @parent node_indirection.find(request) end it "should fail if the parent cannot be found" do @entry[:parent] = "parent" node_indirection.expects(:name2hash).with('parent').returns nil proc { node_indirection.find(request) }.should raise_error(Puppet::Error, /Could not find parent node/) end it "should add any parent classes to the node's classes" do @entry[:parent] = "parent" @entry[:classes] = %w{a b} @parent[:classes] = %w{c d} node_indirection.find(request).classes.should == %w{a b c d} end it "should add any parent parameters to the node's parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["three"] = "four" node_indirection.find(request).parameters.should include({"one" => "two", "three" => "four"}) end it "should prefer node parameters over parent parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["one"] = "three" node_indirection.find(request).parameters.should include({"one" => "two"}) end it "should use the parent's environment if the node has none" do - env = Puppet::Node::Environment.create(:parent, [], '') + env = Puppet::Node::Environment.create(:parent, []) @entry[:parent] = "parent" @parent[:environment] = "parent" Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => env).returns(facts) Puppet.override(:environments => Puppet::Environments::Static.new(env)) do node_indirection.find(request).environment.should == env end end it "should prefer the node's environment to the parent's" do - child_env = Puppet::Node::Environment.create(:child, [], '') + child_env = Puppet::Node::Environment.create(:child, []) @entry[:parent] = "parent" @entry[:environment] = "child" @parent[:environment] = "parent" Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => child_env).returns(facts) Puppet.override(:environments => Puppet::Environments::Static.new(child_env)) do node_indirection.find(request).environment.should == child_env end end it "should recursively look up parent information" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parent] = "parent_parent" @parent[:parameters]["three"] = "four" @parent_parent[:parameters]["five"] = "six" node_indirection.find(request).parameters.should include("one" => "two", "three" => "four", "five" => "six") end it "should not allow loops in parent declarations" do @entry[:parent] = "parent" @parent[:parent] = nodename proc { node_indirection.find(request) }.should raise_error(ArgumentError) end end end end describe "when searching for multiple nodes" do let(:options) { {:environment => environment} } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, options) } before :each do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml end it "should find all nodes if no arguments are provided" do node_indirection.expects(:ldapsearch).with("(objectclass=puppetClient)") # LAK:NOTE The search method requires an essentially bogus key. It's # an API problem that I don't really know how to fix. node_indirection.search request end describe "and a class is specified" do it "should find all nodes that are members of that class" do node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") options[:class] = "one" node_indirection.search request end end describe "multiple classes are specified" do it "should find all nodes that are members of all classes" do node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") options[:class] = %w{one two} node_indirection.search request end end it "should process each found entry" do # .yields can't be used to yield multiple values :/ node_indirection.expects(:ldapsearch).yields("one") node_indirection.expects(:entry2hash).with("one",nil).returns(:name => nodename) node_indirection.search request end it "should return a node for each processed entry with the name from the entry" do node_indirection.expects(:ldapsearch).yields("whatever") node_indirection.expects(:entry2hash).with("whatever",nil).returns(:name => nodename) result = node_indirection.search(request) result[0].should be_instance_of(Puppet::Node) result[0].name.should == nodename end it "should merge each node's facts" do node_indirection.stubs(:ldapsearch).yields("one") node_indirection.stubs(:entry2hash).with("one",nil).returns(:name => nodename) node_indirection.search(request)[0].parameters.should include(fact_values) end it "should pass the request's fqdn option to entry2hash" do options[:fqdn] = :hello node_indirection.stubs(:ldapsearch).yields("one") node_indirection.expects(:entry2hash).with("one",:hello).returns(:name => nodename) node_indirection.search(request) end end describe Puppet::Node::Ldap, " when developing the search query" do it "should return the value of the :ldapclassattrs split on commas as the class attributes" do Puppet[:ldapclassattrs] = "one,two" node_indirection.class_attributes.should == %w{one two} end it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do Puppet[:ldapparentattr] = "" node_indirection.parent_attribute.should be_nil end it "should return the value of the :ldapparentattr as the parent attribute" do Puppet[:ldapparentattr] = "pere" node_indirection.parent_attribute.should == "pere" end it "should use the value of the :ldapstring as the search filter" do Puppet[:ldapstring] = "mystring" node_indirection.search_filter("testing").should == "mystring" end it "should replace '%s' with the node name in the search filter if it is present" do Puppet[:ldapstring] = "my%sstring" node_indirection.search_filter("testing").should == "mytestingstring" end it "should not modify the global :ldapstring when replacing '%s' in the search filter" do filter = mock 'filter' filter.expects(:include?).with("%s").returns(true) filter.expects(:gsub).with("%s", "testing").returns("mynewstring") Puppet[:ldapstring] = filter node_indirection.search_filter("testing").should == "mynewstring" end end describe Puppet::Node::Ldap, " when deciding attributes to search for" do it "should use 'nil' if the :ldapattrs setting is 'all'" do Puppet[:ldapattrs] = "all" node_indirection.search_attributes.should be_nil end it "should split the value of :ldapattrs on commas and use the result as the attribute list" do Puppet[:ldapattrs] = "one,two" node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns(nil) node_indirection.search_attributes.should == %w{one two} end it "should add the class attributes to the search attributes if not returning all attributes" do Puppet[:ldapattrs] = "one,two" node_indirection.stubs(:class_attributes).returns(%w{three four}) node_indirection.stubs(:parent_attribute).returns(nil) # Sort them so i don't have to care about return order node_indirection.search_attributes.sort.should == %w{one two three four}.sort end it "should add the parent attribute to the search attributes if not returning all attributes" do Puppet[:ldapattrs] = "one,two" node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns("parent") node_indirection.search_attributes.sort.should == %w{one two parent}.sort end it "should not add nil parent attributes to the search attributes" do Puppet[:ldapattrs] = "one,two" node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns(nil) node_indirection.search_attributes.should == %w{one two} end end end diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb index 8e1d0decf..607fdefdf 100755 --- a/spec/unit/indirector/node/plain_spec.rb +++ b/spec/unit/indirector/node/plain_spec.rb @@ -1,26 +1,26 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/node/plain' describe Puppet::Node::Plain do let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } - let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } + let(:environment) { Puppet::Node::Environment.create(:myenv, []) } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::Plain.new } before do Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) end it "merges facts into the node" do node_indirection.find(request).parameters.should include(fact_values) end it "should set the node environment from the request" do node_indirection.find(request).environment.should == environment end end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index e0d9df339..3c11664d3 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -1,605 +1,605 @@ #! /usr/bin/env ruby require 'spec_helper' require 'matchers/json' require 'puppet/indirector/request' require 'puppet/util/pson' describe Puppet::Indirector::Request do include JSONMatchers describe "when registering the document type" do it "should register its document type with JSON" do PSON.registered_document_types["IndirectorRequest"].should equal(Puppet::Indirector::Request) end end describe "when initializing" do it "should always convert the indirection name to a symbol" do Puppet::Indirector::Request.new("ind", :method, "mykey", nil).indirection_name.should == :ind end it "should use provided value as the key if it is a string" do Puppet::Indirector::Request.new(:ind, :method, "mykey", nil).key.should == "mykey" end it "should use provided value as the key if it is a symbol" do Puppet::Indirector::Request.new(:ind, :method, :mykey, nil).key.should == :mykey end it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do instance = mock 'instance', :name => "mykey" request = Puppet::Indirector::Request.new(:ind, :method, nil, instance) request.key.should == "mykey" request.instance.should equal(instance) end it "should support options specified as a hash" do expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil, :one => :two) }.to_not raise_error end it "should support nil options" do expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil) }.to_not raise_error end it "should support unspecified options" do expect { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.to_not raise_error end it "should use an empty options hash if nil was provided" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil).options.should == {} end it "should default to a nil node" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).node.should be_nil end it "should set its node attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => "foo.com").node.should == "foo.com" end it "should default to a nil ip" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip.should be_nil end it "should set its ip attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ip => "192.168.0.1").ip.should == "192.168.0.1" end it "should default to being unauthenticated" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_authenticated end it "should set be marked authenticated if configured in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :authenticated => "eh").should be_authenticated end it "should keep its options as a hash even if a node is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => "eh").options.should be_instance_of(Hash) end it "should keep its options as a hash even if another option is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :foo => "bar").options.should be_instance_of(Hash) end it "should treat options other than :ip, :node, and :authenticated as options rather than attributes" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :server => "bar").options[:server].should == "bar" end it "should normalize options to use symbols as keys" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, "foo" => "bar").options[:foo].should == "bar" end describe "and the request key is a URI" do let(:file) { File.expand_path("/my/file with spaces") } describe "and the URI is a 'file' URI" do before do @request = Puppet::Indirector::Request.new(:ind, :method, "#{URI.unescape(Puppet::Util.path_to_uri(file).to_s)}", nil) end it "should set the request key to the unescaped full file path" do @request.key.should == file end it "should not set the protocol" do @request.protocol.should be_nil end it "should not set the port" do @request.port.should be_nil end it "should not set the server" do @request.server.should be_nil end end it "should set the protocol to the URI scheme" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).protocol.should == "http" end it "should set the server if a server is provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).server.should == "host" end it "should set the server and port if both are provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff", nil).port.should == 543 end it "should default to the masterport if the URI scheme is 'puppet'" do Puppet[:masterport] = "321" Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff", nil).port.should == 321 end it "should use the provided port if the URI scheme is not 'puppet'" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).port.should == 80 end it "should set the request key to the unescaped key part path from the URI" do Puppet::Indirector::Request.new(:ind, :method, "http://host/environment/terminus/stuff with spaces", nil).key.should == "stuff with spaces" end it "should set the :uri attribute to the full URI" do Puppet::Indirector::Request.new(:ind, :method, "http:///stu ff", nil).uri.should == 'http:///stu ff' end it "should not parse relative URI" do Puppet::Indirector::Request.new(:ind, :method, "foo/bar", nil).uri.should be_nil end it "should not parse opaque URI" do Puppet::Indirector::Request.new(:ind, :method, "mailto:joe", nil).uri.should be_nil end end it "should allow indication that it should not read a cached instance" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_cache => true).should be_ignore_cache end it "should default to not ignoring the cache" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_ignore_cache end it "should allow indication that it should not not read an instance from the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_terminus => true).should be_ignore_terminus end it "should default to not ignoring the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_ignore_terminus end end it "should look use the Indirection class to return the appropriate indirection" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) request.indirection.should equal(ind) end it "should use its indirection to look up the appropriate model" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) ind.expects(:model).returns "mymodel" request.model.should == "mymodel" end it "should fail intelligently when asked to find a model but the indirection cannot be found" do Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns nil request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) expect { request.model }.to raise_error(ArgumentError) end it "should have a method for determining if the request is plural or singular" do Puppet::Indirector::Request.new(:myind, :method, :key, nil).should respond_to(:plural?) end it "should be considered plural if the method is 'search'" do Puppet::Indirector::Request.new(:myind, :search, :key, nil).should be_plural end it "should not be considered plural if the method is not 'search'" do Puppet::Indirector::Request.new(:myind, :find, :key, nil).should_not be_plural end it "should use its uri, if it has one, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "foo://bar/baz", nil).to_s.should == "foo://bar/baz" end it "should use its indirection name and key, if it has no uri, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "key", nil) == "/myind/key" end it "should be able to return the URI-escaped key" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil).escaped_key.should == URI.escape("my key") end it "should set its environment to an environment instance when a string is specified as its environment" do - env = Puppet::Node::Environment.create(:foo, [], '') + env = Puppet::Node::Environment.create(:foo, []) Puppet.override(:environments => Puppet::Environments::Static.new(env)) do Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").environment.should == env end end it "should use any passed in environment instances as its environment" do - env = Puppet::Node::Environment.create(:foo, [], '') + env = Puppet::Node::Environment.create(:foo, []) Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => env).environment.should equal(env) end it "should use the configured environment when none is provided" do - configured = Puppet::Node::Environment.create(:foo, [], '') + configured = Puppet::Node::Environment.create(:foo, []) Puppet[:environment] = "foo" Puppet.override(:environments => Puppet::Environments::Static.new(configured)) do Puppet::Indirector::Request.new(:myind, :find, "my key", nil).environment.should == configured end end it "should support converting its options to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil ).should respond_to(:to_hash) end it "should include all of its attributes when its options are converted to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :node => 'foo').to_hash[:node].should == 'foo' end describe "when building a query string from its options" do def a_request_with_options(options) Puppet::Indirector::Request.new(:myind, :find, "my key", nil, options) end def the_parsed_query_string_from(request) CGI.parse(request.query_string.sub(/^\?/, '')) end it "should return an empty query string if there are no options" do request = a_request_with_options(nil) request.query_string.should == "" end it "should return an empty query string if the options are empty" do request = a_request_with_options({}) request.query_string.should == "" end it "should prefix the query string with '?'" do request = a_request_with_options(:one => "two") request.query_string.should =~ /^\?/ end it "should include all options in the query string, separated by '&'" do request = a_request_with_options(:one => "two", :three => "four") the_parsed_query_string_from(request).should == { "one" => ["two"], "three" => ["four"] } end it "should ignore nil options" do request = a_request_with_options(:one => "two", :three => nil) the_parsed_query_string_from(request).should == { "one" => ["two"] } end it "should convert 'true' option values into strings" do request = a_request_with_options(:one => true) the_parsed_query_string_from(request).should == { "one" => ["true"] } end it "should convert 'false' option values into strings" do request = a_request_with_options(:one => false) the_parsed_query_string_from(request).should == { "one" => ["false"] } end it "should convert to a string all option values that are integers" do request = a_request_with_options(:one => 50) the_parsed_query_string_from(request).should == { "one" => ["50"] } end it "should convert to a string all option values that are floating point numbers" do request = a_request_with_options(:one => 1.2) the_parsed_query_string_from(request).should == { "one" => ["1.2"] } end it "should CGI-escape all option values that are strings" do request = a_request_with_options(:one => "one two") the_parsed_query_string_from(request).should == { "one" => ["one two"] } end it "should convert an array of values into multiple entries for the same key" do request = a_request_with_options(:one => %w{one two}) the_parsed_query_string_from(request).should == { "one" => ["one", "two"] } end it "should convert an array of values into a single yaml entry when in legacy mode" do Puppet[:legacy_query_parameter_serialization] = true request = a_request_with_options(:one => %w{one two}) the_parsed_query_string_from(request).should == { "one" => ["--- \n - one\n - two"] } end it "should stringify simple data types inside an array" do request = a_request_with_options(:one => ['one', nil]) the_parsed_query_string_from(request).should == { "one" => ["one"] } end it "should error if an array contains another array" do request = a_request_with_options(:one => ['one', ["not allowed"]]) expect { request.query_string }.to raise_error(ArgumentError) end it "should error if an array contains illegal data" do request = a_request_with_options(:one => ['one', { :not => "allowed" }]) expect { request.query_string }.to raise_error(ArgumentError) end it "should convert to a string and CGI-escape all option values that are symbols" do request = a_request_with_options(:one => :"sym bol") the_parsed_query_string_from(request).should == { "one" => ["sym bol"] } end it "should fail if options other than booleans or strings are provided" do request = a_request_with_options(:one => { :one => :two }) expect { request.query_string }.to raise_error(ArgumentError) end end describe "when converting to json" do before do @request = Puppet::Indirector::Request.new(:facts, :find, "foo", nil) end it "should produce a hash with the document_type set to 'request'" do @request.should set_json_document_type_to("IndirectorRequest") end it "should set the 'key'" do @request.should set_json_attribute("key").to("foo") end it "should include an attribute for its indirection name" do @request.should set_json_attribute("type").to("facts") end it "should include a 'method' attribute set to its method" do @request.should set_json_attribute("method").to("find") end it "should add all attributes under the 'attributes' attribute" do @request.ip = "127.0.0.1" @request.should set_json_attribute("attributes", "ip").to("127.0.0.1") end it "should add all options under the 'attributes' attribute" do @request.options["opt"] = "value" PSON.parse(@request.to_pson)["data"]['attributes']['opt'].should == "value" end it "should include the instance if provided" do facts = Puppet::Node::Facts.new("foo") @request.instance = facts PSON.parse(@request.to_pson)["data"]['instance'].should be_instance_of(Hash) end end describe "when converting from json" do before do @request = Puppet::Indirector::Request.new(:facts, :find, "foo", nil) @klass = Puppet::Indirector::Request @format = Puppet::Network::FormatHandler.format('pson') end def from_json(json) @format.intern(Puppet::Indirector::Request, json) end it "should set the 'key'" do from_json(@request.to_pson).key.should == "foo" end it "should fail if no key is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("key") expect { from_json(json.to_pson) }.to raise_error(ArgumentError) end it "should set its indirector name" do from_json(@request.to_pson).indirection_name.should == :facts end it "should fail if no type is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("type") expect { from_json(json.to_pson) }.to raise_error(ArgumentError) end it "should set its method" do from_json(@request.to_pson).method.should == "find" end it "should fail if no method is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("method") expect { from_json(json.to_pson) }.to raise_error(ArgumentError) end it "should initialize with all attributes and options" do @request.ip = "127.0.0.1" @request.options["opt"] = "value" result = from_json(@request.to_pson) result.options[:opt].should == "value" result.ip.should == "127.0.0.1" end it "should set its instance as an instance if one is provided" do facts = Puppet::Node::Facts.new("foo") @request.instance = facts result = from_json(@request.to_pson) result.instance.should be_instance_of(Puppet::Node::Facts) end end context '#do_request' do before :each do @request = Puppet::Indirector::Request.new(:myind, :find, "my key", nil) end context 'when not using SRV records' do before :each do Puppet.settings[:use_srv_records] = false end it "yields the request with the default server and port when no server or port were specified on the original request" do count = 0 rval = @request.do_request(:puppet, 'puppet.example.com', '90210') do |got| count += 1 got.server.should == 'puppet.example.com' got.port.should == '90210' 'Block return value' end count.should == 1 rval.should == 'Block return value' end end context 'when using SRV records' do before :each do Puppet.settings[:use_srv_records] = true Puppet.settings[:srv_domain] = 'example.com' end it "yields the request with the original server and port unmodified" do @request.server = 'puppet.example.com' @request.port = '90210' count = 0 rval = @request.do_request do |got| count += 1 got.server.should == 'puppet.example.com' got.port.should == '90210' 'Block return value' end count.should == 1 rval.should == 'Block return value' end context "when SRV returns servers" do before :each do @dns_mock = mock('dns') Resolv::DNS.expects(:new).returns(@dns_mock) @port = 7205 @host = '_x-puppet._tcp.example.com' @srv_records = [Resolv::DNS::Resource::IN::SRV.new(0, 0, @port, @host)] @dns_mock.expects(:getresources). with("_x-puppet._tcp.#{Puppet.settings[:srv_domain]}", Resolv::DNS::Resource::IN::SRV). returns(@srv_records) end it "yields a request using the server and port from the SRV record" do count = 0 rval = @request.do_request do |got| count += 1 got.server.should == '_x-puppet._tcp.example.com' got.port.should == 7205 @block_return end count.should == 1 rval.should == @block_return end it "should fall back to the default server when the block raises a SystemCallError" do count = 0 second_pass = nil rval = @request.do_request(:puppet, 'puppet', 8140) do |got| count += 1 if got.server == '_x-puppet._tcp.example.com' then raise SystemCallError, "example failure" else second_pass = got end @block_return end second_pass.server.should == 'puppet' second_pass.port.should == 8140 count.should == 2 rval.should == @block_return end end end end describe "#remote?" do def request(options = {}) Puppet::Indirector::Request.new('node', 'find', 'localhost', nil, options) end it "should not be unless node or ip is set" do request.should_not be_remote end it "should be remote if node is set" do request(:node => 'example.com').should be_remote end it "should be remote if ip is set" do request(:ip => '127.0.0.1').should be_remote end it "should be remote if node and ip are set" do request(:node => 'example.com', :ip => '127.0.0.1').should be_remote end end end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 8e3c46989..b8c3fdeea 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -1,710 +1,710 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/files' require 'puppet_spec/modules' require 'puppet/module_tool/checksums' describe Puppet::Module do include PuppetSpec::Files let(:env) { mock("environment") } let(:path) { "/path" } let(:name) { "mymod" } let(:mod) { Puppet::Module.new(name, path, env) } before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory Puppet::FileSystem.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do env = mock 'module' env.expects(:module).with(name).returns "yep" Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find(name, "myenv").should == "yep" end it "should return nil if asked for a named module that doesn't exist" do env = mock 'module' env.expects(:module).with(name).returns nil Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find(name, "myenv").should be_nil end describe "attributes" do it "should support a 'version' attribute" do mod.version = 1.09 mod.version.should == 1.09 end it "should support a 'source' attribute" do mod.source = "http://foo/bar" mod.source.should == "http://foo/bar" end it "should support a 'project_page' attribute" do mod.project_page = "http://foo/bar" mod.project_page.should == "http://foo/bar" end it "should support an 'author' attribute" do mod.author = "Luke Kanies " mod.author.should == "Luke Kanies " end it "should support a 'license' attribute" do mod.license = "GPL2" mod.license.should == "GPL2" end it "should support a 'summary' attribute" do mod.summary = "GPL2" mod.summary.should == "GPL2" end it "should support a 'description' attribute" do mod.description = "GPL2" mod.description.should == "GPL2" end it "should support specifying a compatible puppet version" do mod.puppetversion = "0.25" mod.puppetversion.should == "0.25" end end it "should validate that the puppet version is compatible" do mod.puppetversion = "0.25" Puppet.expects(:version).returns "0.25" mod.validate_puppet_version end it "should fail if the specified puppet version is not compatible" do mod.puppetversion = "0.25" Puppet.stubs(:version).returns "0.24" lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end describe "when finding unmet dependencies" do before do Puppet::FileSystem.unstub(:exist?) @modpath = tmpdir('modpath') Puppet.settings[:modulepath] = @modpath end it "should list modules that are missing" do metadata_file = "#{@modpath}/needy/metadata.json" mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules that are missing and have invalid names" do metadata_file = "#{@modpath}/needy/metadata.json" mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar=bar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar=bar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules with unmet version requirement" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) mod = PuppetSpec::Modules.create( 'test_gte_req', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] }, :environment => env ) mod2 = PuppetSpec::Modules.create( 'test_specific_req', @modpath, :metadata => { :dependencies => [{ "version_requirement" => "1.0.0", "name" => "baz/foobar" }] }, :environment => env ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' }, :environment => env ) mod.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/test_gte_req" }, :mod_details => { :installed_version => "2.0.0" } }] mod2.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => "v1.0.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/test_specific_req" }, :mod_details => { :installed_version => "2.0.0" } }] end it "should consider a dependency without a version requirement to be satisfied" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] }, :environment => env ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' }, :environment => env ) mod.unmet_dependencies.should be_empty end it "should consider a dependency without a semantic version to be unmet" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] }, :environment => env ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '5.1', :author => 'baz' }, :environment => env ) mod.unmet_dependencies.should == [{ :reason => :non_semantic_version, :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, :mod_details => { :installed_version => "5.1" }, :name => "baz/foobar", :version_constraint => ">= 0.0.0" }] end it "should have valid dependencies when no dependencies have been specified" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [] } ) mod.unmet_dependencies.should == [] end it "should only list unmet dependencies" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) mod = PuppetSpec::Modules.create( name, @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => ">= 2.2.0", "name" => "baz/notsatisfied" } ] }, :environment => env ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' }, :environment => env ) mod.unmet_dependencies.should == [{ :reason => :missing, :mod_details => { :installed_version => nil }, :parent => { :version => "v9.9.9", :name => "puppetlabs/#{name}" }, :name => "baz/notsatisfied", :version_constraint => ">= 2.2.0" }] end it "should be empty when all dependencies are met" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) mod = PuppetSpec::Modules.create( 'mymod2', @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => "< 2.2.0", "name" => "baz/alsosatisfied" } ] }, :environment => env ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' }, :environment => env ) PuppetSpec::Modules.create( 'alsosatisfied', @modpath, :metadata => { :version => '2.1.0', :author => 'baz' }, :environment => env ) mod.unmet_dependencies.should be_empty end end describe "when managing supported platforms" do it "should support specifying a supported platform" do mod.supports "solaris" end it "should support specifying a supported platform and version" do mod.supports "solaris", 1.0 end end it "should return nil if asked for a module whose name is 'nil'" do Puppet::Module.find(nil, "myenv").should be_nil end it "should provide support for logging" do Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) end it "should be able to be converted to a string" do mod.to_s.should == "Module #{name}(#{path})" end it "should fail if its name is not alphanumeric" do lambda { Puppet::Module.new(".something", "/path", env) }.should raise_error(Puppet::Module::InvalidName) end it "should require a name at initialization" do lambda { Puppet::Module.new }.should raise_error(ArgumentError) end it "should accept an environment at initialization" do Puppet::Module.new("foo", "/path", env).environment.should == env end describe '#modulepath' do it "should return the directory the module is installed in, if a path exists" do mod = Puppet::Module.new("foo", "/a/foo", env) mod.modulepath.should == '/a' end end [:plugins, :pluginfacts, :templates, :files, :manifests].each do |filetype| case filetype when :plugins dirname = "lib" when :pluginfacts dirname = "facts.d" else dirname = filetype.to_s end it "should be able to return individual #{filetype}" do module_file = File.join(path, dirname, "my/file") Puppet::FileSystem.expects(:exist?).with(module_file).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == module_file end it "should consider #{filetype} to be present if their base directory exists" do module_file = File.join(path, dirname) Puppet::FileSystem.expects(:exist?).with(module_file).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do module_file = File.join(path, dirname) Puppet::FileSystem.expects(:exist?).with(module_file).returns false mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do module_file = File.join(path, dirname, "my/file") Puppet::FileSystem.expects(:exist?).with(module_file).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do base = File.join(path, dirname) Puppet::FileSystem.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end it "should return the path to the plugin directory" do mod.plugin_directory.should == File.join(path, "lib") end end describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod", "/a", mock("environment")) @pq_glob_with_extension = "yay/*.xx" @fq_glob_with_extension = "/a/manifests/#{@pq_glob_with_extension}" end it "should return all manifests matching the glob pattern" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.stubs(:directory?).returns false @mod.match_manifests(@pq_glob_with_extension).should == %w{foo bar} end it "should not return directories" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.expects(:directory?).with("foo").returns false FileTest.expects(:directory?).with("bar").returns true @mod.match_manifests(@pq_glob_with_extension).should == %w{foo} end it "should default to the 'init' file if no glob pattern is specified" do Puppet::FileSystem.expects(:exist?).with("/a/manifests/init.pp").returns(true) Puppet::FileSystem.expects(:exist?).with("/a/manifests/init.rb").returns(false) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end it "should return all manifests matching the glob pattern in all existing paths" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{a b}) @mod.match_manifests(@pq_glob_with_extension).should == %w{a b} end it "should match the glob pattern plus '.{pp,rb}' if no extention is specified" do Dir.expects(:glob).with("/a/manifests/yay/foo.{pp,rb}").returns(%w{yay}) @mod.match_manifests("yay/foo").should == %w{yay} end it "should return an empty array if no manifests matched" do Dir.expects(:glob).with(@fq_glob_with_extension).returns([]) @mod.match_manifests(@pq_glob_with_extension).should == [] end it "should raise an error if the pattern tries to leave the manifest directory" do expect do @mod.match_manifests("something/../../*") end.to raise_error(Puppet::Module::InvalidFilePattern, 'The pattern "something/../../*" to find manifests in the module "mymod" is invalid and potentially unsafe.') end end describe Puppet::Module do include PuppetSpec::Files before do @modpath = tmpdir('modpath') @module = PuppetSpec::Modules.create('mymod', @modpath) end it "should use 'License' in its current path as its metadata file" do @module.license_file.should == "#{@modpath}/mymod/License" end it "should cache the license file" do @module.expects(:path).once.returns nil @module.license_file @module.license_file end it "should use 'metadata.json' in its current path as its metadata file" do @module.metadata_file.should == "#{@modpath}/mymod/metadata.json" end it "should have metadata if it has a metadata file and its data is not empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should have metadata if it has a metadata file and its data is not empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should not have metadata if has a metadata file and its data is empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "/* +-----------------------------------------------------------------------+ | | | ==> DO NOT EDIT THIS FILE! <== | | | | You should edit the `Modulefile` and run `puppet-module build` | | to generate the `metadata.json` file for your releases. | | | +-----------------------------------------------------------------------+ */ {}" @module.should_not be_has_metadata end it "should know if it is missing a metadata file" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end it "should be able to parse its metadata file" do @module.should respond_to(:load_metadata) end it "should parse its metadata file on initialization if it is present" do Puppet::Module.any_instance.expects(:has_metadata?).returns true Puppet::Module.any_instance.expects(:load_metadata) Puppet::Module.new("yay", "/path", mock("env")) end it "should tolerate failure to parse" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns(my_fixture('trailing-comma.json')) @module.has_metadata?.should be_false end def a_module_with_metadata(data) text = data.to_pson mod = Puppet::Module.new("foo", "/path", mock("env")) mod.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns text mod end describe "when loading the metadata file" do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [] } @module = a_module_with_metadata(@data) end %w{source author version license}.each do |attr| it "should set #{attr} if present in the metadata file" do @module.load_metadata @module.send(attr).should == @data[attr.to_sym] end it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_pson File.stubs(:read).with("/my/file").returns @text lambda { @module.load_metadata }.should raise_error( Puppet::Module::MissingMetadata, "No #{attr} module metadata provided for foo" ) end end it "should set puppetversion if present in the metadata file" do @module.load_metadata @module.puppetversion.should == @data[:puppetversion] end context "when versionRequirement is used for dependency version info" do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [ { "versionRequirement" => "0.0.1", "name" => "pmtacceptance/stdlib" }, { "versionRequirement" => "0.1.0", "name" => "pmtacceptance/apache" } ] } @module = a_module_with_metadata(@data) end it "should set the dependency version_requirement key" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" end it "should set the version_requirement key for all dependencies" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" @module.dependencies[1]['version_requirement'].should == "0.1.0" end end end it "should be able to tell if there are local changes" do modpath = tmpdir('modpath') foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8' checksummed_module = PuppetSpec::Modules.create( 'changed', modpath, :metadata => { :checksums => { "foo" => foo_checksum, } } ) foo_path = Pathname.new(File.join(checksummed_module.path, 'foo')) IO.binwrite(foo_path, 'notfoo') Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should_not == foo_checksum checksummed_module.has_local_changes?.should be_true IO.binwrite(foo_path, 'foo') Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should == foo_checksum checksummed_module.has_local_changes?.should be_false end it "should know what other modules require it" do - env = Puppet::Node::Environment.create(:testing, [@modpath], '') + env = Puppet::Node::Environment.create(:testing, [@modpath]) dependable = PuppetSpec::Modules.create( 'dependable', @modpath, :metadata => {:author => 'puppetlabs'}, :environment => env ) PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "puppetlabs/dependable" }] }, :environment => env ) PuppetSpec::Modules.create( 'wantit', @modpath, :metadata => { :author => 'spoiled', :dependencies => [{ "version_requirement" => "< 5.0.0", "name" => "puppetlabs/dependable" }] }, :environment => env ) dependable.required_by.should =~ [ { "name" => "beggar/needy", "version" => "9.9.9", "version_requirement" => ">= 2.2.0" }, { "name" => "spoiled/wantit", "version" => "9.9.9", "version_requirement" => "< 5.0.0" } ] end end diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index dc6d7b009..63c8cf48e 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -1,331 +1,331 @@ require 'spec_helper' require 'puppet/module_tool/applications' require 'puppet_spec/modules' require 'semver' describe Puppet::ModuleTool::Applications::Installer do include PuppetSpec::Files let(:unpacker) { stub(:run) } let(:installer_class) { Puppet::ModuleTool::Applications::Installer } let(:modpath1) do path = File.join(tmpdir("installer"), "modpath1") FileUtils.mkdir_p(path) path end let(:stdlib_pkg) do mod = File.join(modpath1, "pmtacceptance-stdlib-0.0.1.tar.gz") FileUtils.touch(mod) mod end - let(:env) { Puppet::Node::Environment.create(:env, [modpath1], '') } + let(:env) { Puppet::Node::Environment.create(:env, [modpath1]) } let(:options) do { :target_dir => modpath1, :environment_instance => env, } end let(:forge) do forge = mock("Puppet::Forge") forge.stubs(:remote_dependency_info).returns(remote_dependency_info) forge.stubs(:uri).returns('forge-dev.puppetlabs.com') remote_dependency_info.each_key do |mod| remote_dependency_info[mod].each do |release| forge.stubs(:retrieve).with(release['file']).returns("/fake_cache#{release['file']}") end end forge end let(:install_dir) do install_dir = mock("Puppet::ModuleTool::InstallDirectory") install_dir.stubs(:prepare) install_dir end let(:remote_dependency_info) do { "pmtacceptance/apache" => [ { "dependencies" => [], "version" => "1.0.0-alpha", "file" => "/pmtacceptance-apache-1.0.0-alpha.tar.gz" }, { "dependencies" => [], "version" => "1.0.0-beta", "file" => "/pmtacceptance-apache-1.0.0-beta.tar.gz" }, { "dependencies" => [], "version" => "1.0.0-rc1", "file" => "/pmtacceptance-apache-1.0.0-rc1.tar.gz" }, ], "pmtacceptance/stdlib" => [ { "dependencies" => [], "version" => "0.0.1", "file" => "/pmtacceptance-stdlib-0.0.1.tar.gz" }, { "dependencies" => [], "version" => "0.0.2", "file" => "/pmtacceptance-stdlib-0.0.2.tar.gz" }, { "dependencies" => [], "version" => "1.0.0-pre", "file" => "/pmtacceptance-stdlib-1.0.0-pre.tar.gz" }, { "dependencies" => [], "version" => "1.0.0", "file" => "/pmtacceptance-stdlib-1.0.0.tar.gz" }, { "dependencies" => [], "version" => "1.5.0-pre", "file" => "/pmtacceptance-stdlib-1.5.0-pre.tar.gz" }, ], "pmtacceptance/java" => [ { "dependencies" => [["pmtacceptance/stdlib", ">= 0.0.1"]], "version" => "1.7.0", "file" => "/pmtacceptance-java-1.7.0.tar.gz" }, { "dependencies" => [["pmtacceptance/stdlib", "1.0.0"]], "version" => "1.7.1", "file" => "/pmtacceptance-java-1.7.1.tar.gz" } ], "pmtacceptance/apollo" => [ { "dependencies" => [ ["pmtacceptance/java", "1.7.1"], ["pmtacceptance/stdlib", "0.0.1"] ], "version" => "0.0.1", "file" => "/pmtacceptance-apollo-0.0.1.tar.gz" }, { "dependencies" => [ ["pmtacceptance/java", ">= 1.7.0"], ["pmtacceptance/stdlib", ">= 1.0.0"] ], "version" => "0.0.2", "file" => "/pmtacceptance-apollo-0.0.2.tar.gz" } ] } end describe "the behavior of .is_module_package?" do it "should return true when file is a module package" do installer = installer_class.new("foo", forge, install_dir, options) installer.send(:is_module_package?, stdlib_pkg).should be_true end it "should return false when file is not a module package" do installer = installer_class.new("foo", forge, install_dir, options) installer.send(:is_module_package?, "pmtacceptance-apollo-0.0.2.tar"). should be_false end end context "when the source is a repository" do it "should require a valid name" do lambda { installer_class.run('puppet', install_dir, params) }.should raise_error(ArgumentError, "Could not install module with invalid name: puppet") end it "should install the current stable version of the requested module" do Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-stdlib', forge, install_dir, options) results[:installed_modules].length == 1 results[:installed_modules][0][:module].should == "pmtacceptance-stdlib" results[:installed_modules][0][:version][:vstring].should == "1.0.0" end it "should install the most recent version of requested module in the absence of a stable version" do Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apache-1.0.0-rc1.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-apache', forge, install_dir, options) results[:installed_modules].length == 1 results[:installed_modules][0][:module].should == "pmtacceptance-apache" results[:installed_modules][0][:version][:vstring].should == "1.0.0-rc1" end it "should install the most recent stable version of requested module for the requested version range" do Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options.merge(:version => '1.x')). returns(unpacker) results = installer_class.run('pmtacceptance-stdlib', forge, install_dir, options.merge(:version => '1.x')) results[:installed_modules].length == 1 results[:installed_modules][0][:module].should == "pmtacceptance-stdlib" results[:installed_modules][0][:version][:vstring].should == "1.0.0" end it "should install the most recent version of requested module for the requested version range in the absence of a stable version" do Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.5.0-pre.tar.gz', options.merge(:version => '1.5.0-pre')). returns(unpacker) results = installer_class.run('pmtacceptance-stdlib', forge, install_dir, options.merge(:version => '1.5.0-pre')) results[:installed_modules].length == 1 results[:installed_modules][0][:module].should == "pmtacceptance-stdlib" results[:installed_modules][0][:version][:vstring].should == "1.5.0-pre" end context "should check the target directory" do let(:installer) do installer_class.new('pmtacceptance-stdlib', forge, install_dir, options) end def expect_normal_unpacker Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options). returns(unpacker) end def expect_normal_results results end it "(#15202) prepares the install directory" do expect_normal_unpacker install_dir.expects(:prepare).with("pmtacceptance-stdlib", "latest") results = installer.run results[:installed_modules].length.should eq 1 results[:installed_modules][0][:module].should == "pmtacceptance-stdlib" results[:installed_modules][0][:version][:vstring].should == "1.0.0" end it "(#15202) reports an error when the install directory cannot be prepared" do install_dir.expects(:prepare).with("pmtacceptance-stdlib", "latest"). raises(Puppet::ModuleTool::Errors::PermissionDeniedCreateInstallDirectoryError.new("original", :module => "pmtacceptance-stdlib")) results = installer.run results[:result].should == :failure results[:error][:oneline].should =~ /Permission is denied/ end end context "when the requested module has dependencies" do it "should install dependencies" do Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options). returns(unpacker) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-java-1.7.1.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-apollo', forge, install_dir, options) installed_dependencies = results[:installed_modules][0][:dependencies] dependencies = installed_dependencies.inject({}) do |result, dep| result[dep[:module]] = dep[:version][:vstring] result end dependencies.length.should == 2 dependencies['pmtacceptance-java'].should == '1.7.1' dependencies['pmtacceptance-stdlib'].should == '1.0.0' end it "should install requested module if the '--force' flag is used" do options.merge!(:force => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-apollo', forge, install_dir, options) results[:installed_modules][0][:module].should == "pmtacceptance-apollo" end it "should not install dependencies if the '--force' flag is used" do options.merge!(:force => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-apollo', forge, install_dir, options) dependencies = results[:installed_modules][0][:dependencies] dependencies.should == [] end it "should not install dependencies if the '--ignore-dependencies' flag is used" do options.merge!(:ignore_dependencies => true) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) results = installer_class.run('pmtacceptance-apollo', forge, install_dir, options) dependencies = results[:installed_modules][0][:dependencies] dependencies.should == [] end it "should set an error if dependencies can't be resolved" do options.merge!(:version => '0.0.1') oneline = "'pmtacceptance-apollo' (v0.0.1) requested; Invalid dependency cycle" multiline = <<-MSG.strip Could not install module 'pmtacceptance-apollo' (v0.0.1) No version of 'pmtacceptance-stdlib' will satisfy dependencies You specified 'pmtacceptance-apollo' (v0.0.1), which depends on 'pmtacceptance-java' (v1.7.1), which depends on 'pmtacceptance-stdlib' (v1.0.0) Use `puppet module install --force` to install this module anyway MSG results = installer_class.run('pmtacceptance-apollo', forge, install_dir, options) results[:result].should == :failure results[:error][:oneline].should == oneline results[:error][:multiline].should == multiline end it "resolves conflicts for each dependency only once" do Puppet::Log.level = :debug installed_modules = [ Puppet::Module.new('ntp', File.join(modpath1, 'ntp'), env.name), Puppet::Module.new('mysql', File.join(modpath1, 'mysql'), env.name), Puppet::Module.new('apache', File.join(modpath1, 'apache'), env.name) ] env.stubs(:modules_by_path).returns({modpath1 => installed_modules}) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options). returns(unpacker) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-java-1.7.1.tar.gz', options). returns(unpacker) Puppet::ModuleTool::Applications::Unpacker.expects(:new). with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options). returns(unpacker) installer_class.run('pmtacceptance-apollo', forge, install_dir, options) modules = @logs.map do |log| data = log.message.match(/Resolving conflicts for (.*)/) data ? data[1] : nil end.compact expect(modules).to eq(["pmtacceptance-apollo", "pmtacceptance-java,pmtacceptance-stdlib"]) end end context "when there are modules installed" do it "should use local version when already exists and satisfies constraints" it "should reinstall the local version if force is used" it "should upgrade local version when necessary to satisfy constraints" it "should error when a local version can't be upgraded to satisfy constraints" end context "when a local module needs upgrading to satisfy constraints but has changes" do it "should error" it "should warn and continue if force is used" end it "should error when a local version of a dependency has no version metadata" it "should error when a local version of a dependency has a non-semver version" it "should error when a local version of a dependency has a different forge name" it "should error when a local version of a dependency has no metadata" end context "when the source is a filesystem" do before do @sourcedir = tmpdir('sourcedir') end it "should error if it can't parse the name" it "should try to get_release_package_from_filesystem if it has a valid name" end end diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index f7b1d9bba..2ec65c6e4 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -1,207 +1,207 @@ require 'spec_helper' require 'puppet/module_tool' require 'tmpdir' require 'puppet_spec/modules' describe Puppet::ModuleTool::Applications::Uninstaller do include PuppetSpec::Files def mkmod(name, path, metadata=nil) modpath = File.join(path, name) FileUtils.mkdir_p(modpath) if metadata File.open(File.join(modpath, 'metadata.json'), 'w') do |f| f.write(metadata.to_pson) end end modpath end describe "the behavior of the instances" do let(:working_dir) { tmpdir("uninstaller") } let(:modpath1) { create_temp_dir("modpath1") } let(:modpath2) { create_temp_dir("modpath2") } - let(:env) { Puppet::Node::Environment.create(:env, [modpath1, modpath2], '') } + let(:env) { Puppet::Node::Environment.create(:env, [modpath1, modpath2]) } let(:options) { { :environment_instance => env } } let(:uninstaller) { Puppet::ModuleTool::Applications::Uninstaller.new("puppetlabs-foo", options) } def create_temp_dir(name) path = File.join(working_dir, name) FileUtils.mkdir_p(path) path end let(:foo_metadata) do { :author => "puppetlabs", :name => "puppetlabs/foo", :version => "1.0.0", :source => "http://dummyurl/foo", :license => "Apache2", :dependencies => [], } end let(:bar_metadata) do { :author => "puppetlabs", :name => "puppetlabs/bar", :version => "1.0.0", :source => "http://dummyurl/bar", :license => "Apache2", :dependencies => [], } end context "when the module is not installed" do it "should fail" do Puppet::ModuleTool::Applications::Uninstaller.new('fakemod_not_installed', options).run[:result].should == :failure end end context "when the module is installed" do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) results = uninstaller.run results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should only uninstall the requested module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('bar', modpath1, :metadata => bar_metadata) results = uninstaller.run results[:affected_modules].length == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should uninstall fail if a module exists twice in the modpath" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('foo', modpath2, :metadata => foo_metadata) uninstaller.run[:result].should == :failure end context "when options[:version] is specified" do it "should uninstall the module if the version matches" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) options[:version] = "1.0.0" results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" results[:affected_modules].first.version.should == "1.0.0" end it "should not uninstall the module if the version does not match" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) options[:version] = "2.0.0" uninstaller.run[:result].should == :failure end end context "when the module metadata is missing" do it "should not uninstall the module" do PuppetSpec::Modules.create('foo', modpath1) uninstaller.run[:result].should == :failure end end context "when the module has local changes" do it "should not uninstall the module" do mod = PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) Puppet::ModuleTool::Applications::Checksummer.expects(:run).with(mod.path).returns(['change']) uninstaller.run[:result].should == :failure end end context "when the module does not have local changes" do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end end context "when uninstalling the module will cause broken dependencies" do it "should not uninstall the module" do Puppet.settings[:modulepath] = modpath1 PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create( 'needy', modpath1, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 1.0.0", "name" => "puppetlabs/foo" }] } ) uninstaller.run[:result].should == :failure end end context "when using the --force flag" do let(:fakemod) do stub( :forge_name => 'puppetlabs/fakemod', :version => '0.0.1', :has_local_changes? => true ) end it "should ignore local changes" do foo = mkmod("foo", modpath1, foo_metadata) options[:force] = true results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should ignore broken dependencies" do Puppet.settings[:modulepath] = modpath1 PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create( 'needy', modpath1, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 1.0.0", "name" => "puppetlabs/foo" }] } ) options[:force] = true results = uninstaller.run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end end end end end diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index 63bd5e295..083cf74ce 100755 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -1,204 +1,204 @@ #! /usr/bin/env ruby # encoding: UTF-8 require 'spec_helper' require 'puppet/module_tool' describe Puppet::ModuleTool do describe '.is_module_root?' do it 'should return true if directory has a module file' do FileTest.expects(:file?).with(responds_with(:to_s, '/a/b/c/Modulefile')). returns(true) subject.is_module_root?(Pathname.new('/a/b/c')).should be_true end it 'should return false if directory does not have a module file' do FileTest.expects(:file?).with(responds_with(:to_s, '/a/b/c/Modulefile')). returns(false) subject.is_module_root?(Pathname.new('/a/b/c')).should be_false end end describe '.find_module_root' do let(:sample_path) { Pathname.new('/a/b/c').expand_path } it 'should return the first path as a pathname when it contains a module file' do Puppet::ModuleTool.expects(:is_module_root?).with(sample_path). returns(true) subject.find_module_root(sample_path).should == sample_path end it 'should return a parent path as a pathname when it contains a module file' do Puppet::ModuleTool.expects(:is_module_root?). with(responds_with(:to_s, File.expand_path('/a/b/c'))).returns(false) Puppet::ModuleTool.expects(:is_module_root?). with(responds_with(:to_s, File.expand_path('/a/b'))).returns(true) subject.find_module_root(sample_path).should == Pathname.new('/a/b').expand_path end it 'should return nil when no module root can be found' do Puppet::ModuleTool.expects(:is_module_root?).at_least_once.returns(false) subject.find_module_root(sample_path).should be_nil end end describe '.format_tree' do it 'should return an empty tree when given an empty list' do subject.format_tree([]).should == '' end it 'should return a shallow when given a list without dependencies' do list = [ { :text => 'first' }, { :text => 'second' }, { :text => 'third' } ] subject.format_tree(list).should == <<-TREE ├── first ├── second └── third TREE end it 'should return a deeply nested tree when given a list with deep dependencies' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, ] subject.format_tree(list).should == <<-TREE └─┬ first └─┬ second └── third TREE end it 'should show connectors when deep dependencies are not on the last node of the top level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, { :text => 'fourth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ └─┬ second │ └── third └── fourth TREE end it 'should show connectors when deep dependencies are not on the last node of any level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] } ] subject.format_tree(list).should == <<-TREE └─┬ first ├─┬ second │ └── third └── fourth TREE end it 'should show connectors in every case when deep dependencies are not on the last node' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] }, { :text => 'fifth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ ├─┬ second │ │ └── third │ └── fourth └── fifth TREE end end describe '.set_option_defaults' do let(:options) { {} } let(:modulepath) { [File.expand_path('/env/module/path'), File.expand_path('/global/module/path')] } - let(:environment) { Puppet::Node::Environment.create('current', modulepath, '') } + let(:environment) { Puppet::Node::Environment.create('current', modulepath) } around(:each) do |example| Puppet.override(:current_environment => environment) do example.run end end describe 'option :environment_instance' do it 'adds an environment_instance to the options hash' do subject.set_option_defaults(options) expect(options[:environment_instance].name).to eq(environment.name) expect(options[:environment_instance].modulepath).to eq(environment.modulepath) end end describe 'option :target_dir' do let (:target_dir) { 'boo' } context 'passed:' do let (:options) { {:target_dir => target_dir} } it 'prepends the target_dir into the environment_instance modulepath' do subject.set_option_defaults options expect(options[:environment_instance].full_modulepath). to eq([File.expand_path(target_dir)] + modulepath) end it 'expands the target dir' do subject.set_option_defaults options options[:target_dir].should == File.expand_path(target_dir) end end context 'NOT passed:' do it 'the option should be set to the first component of Puppet[:modulepath]' do options = Hash.new subject.set_option_defaults options expect(options[:target_dir]).to eq(environment.full_modulepath.first) end end end end end diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb index 6e97f905d..6c6d7a581 100644 --- a/spec/unit/network/http/api/v2/environments_spec.rb +++ b/spec/unit/network/http/api/v2/environments_spec.rb @@ -1,42 +1,42 @@ require 'spec_helper' require 'puppet/node/environment' require 'puppet/network/http' require 'matchers/json' describe Puppet::Network::HTTP::API::V2::Environments do include JSONMatchers it "responds with all of the available environments" do environment = Puppet::Node::Environment.create(:production, ["/first", "/second"], '/manifests') loader = Puppet::Environments::Static.new(environment) handler = Puppet::Network::HTTP::API::V2::Environments.new(loader) response = Puppet::Network::HTTP::MemoryResponse.new handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) expect(response.code).to eq(200) expect(response.type).to eq("application/json") expect(JSON.parse(response.body)).to eq({ "search_paths" => loader.search_paths, "environments" => { "production" => { "settings" => { "modulepath" => [File.expand_path("/first"), File.expand_path("/second")], "manifest" => File.expand_path("/manifests") } } } }) end it "the response conforms to the environments schema" do - environment = Puppet::Node::Environment.create(:production, [], '') + environment = Puppet::Node::Environment.create(:production, []) handler = Puppet::Network::HTTP::API::V2::Environments.new(Puppet::Environments::Static.new(environment)) response = Puppet::Network::HTTP::MemoryResponse.new handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response) expect(response.body).to validate_against('api/schemas/environments.json') end end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 5d265e00b..b5728ac5a 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -1,466 +1,466 @@ #! /usr/bin/env ruby require 'spec_helper' require 'tmpdir' require 'puppet/node/environment' require 'puppet/util/execution' require 'puppet_spec/modules' require 'puppet/parser/parser_factory' describe Puppet::Node::Environment do let(:env) { Puppet::Node::Environment.new("testing") } include PuppetSpec::Files after do Puppet::Node::Environment.clear end shared_examples_for 'the environment' do 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 default environment if no name is provided while initializing an environment" do Puppet[:environment] = "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 it "should just return any provided environment if an environment is provided as the name" do one = Puppet::Node::Environment.new(:one) Puppet::Node::Environment.new(one).should equal(one) end describe "overriding an existing environment" do let(:original_path) { [tmpdir('original')] } let(:new_path) { [tmpdir('new')] } let(:environment) { Puppet::Node::Environment.create(:overridden, original_path, 'orig.pp') } it "overrides modulepath" do overridden = environment.override_with(:modulepath => new_path) expect(overridden).to_not be_equal(environment) expect(overridden.name).to eq(:overridden) expect(overridden.manifest).to eq(File.expand_path('orig.pp')) expect(overridden.modulepath).to eq(new_path) end it "overrides manifest" do overridden = environment.override_with(:manifest => 'new.pp') expect(overridden).to_not be_equal(environment) expect(overridden.name).to eq(:overridden) expect(overridden.manifest).to eq(File.expand_path('new.pp')) expect(overridden.modulepath).to eq(original_path) end end describe "watching a file" do let(:filename) { "filename" } it "accepts a File" do file = tmpfile(filename) env.known_resource_types.expects(:watch_file).with(file.to_s) env.watch_file(file) end it "accepts a String" do env.known_resource_types.expects(:watch_file).with(filename) env.watch_file(filename) end end describe "when managing known resource types" do before do @collection = Puppet::Resource::TypeCollection.new(env) env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) end it "should create a resource type collection if none exists" do 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 env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection env.known_resource_types.stubs(:stale?).returns true env.known_resource_types.should equal(@collection) end it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true env.check_for_reparse new_type_collection = env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end end it "should validate the modulepath directories" do real_file = tmpdir('moduledir') path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR) Puppet[:modulepath] = path env.modulepath.should == [real_file] end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do first_puppetlib = tmpdir('puppetlib1') second_puppetlib = tmpdir('puppetlib2') first_moduledir = tmpdir('moduledir1') second_moduledir = tmpdir('moduledir2') Puppet::Util.withenv("PUPPETLIB" => [first_puppetlib, second_puppetlib].join(File::PATH_SEPARATOR)) do Puppet[:modulepath] = [first_moduledir, second_moduledir].join(File::PATH_SEPARATOR) env.modulepath.should == [first_puppetlib, second_puppetlib, first_moduledir, second_moduledir] 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.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.parse_config(<<-CONF) [testing] server = myval CONF env[:server].should == "myval" end it "should be able to return an individual module that exists in its module path" do env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))] mod = env.module('one') mod.should be_a(Puppet::Module) mod.name.should == 'one' end it "should not return a module if the module doesn't exist" do env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))] env.module('two').should be_nil end it "should return nil if asked for a module that does not exist in its path" do modpath = tmpdir('modpath') - env = Puppet::Node::Environment.create(:testing, [modpath], '') + env = Puppet::Node::Environment.create(:testing, [modpath]) env.module("one").should be_nil end describe "module data" do before do dir = tmpdir("deep_path") @first = File.join(dir, "first") @second = File.join(dir, "second") Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" FileUtils.mkdir_p(@first) FileUtils.mkdir_p(@second) end describe "#modules_by_path" do it "should return an empty list if there are no modules" do env.modules_by_path.should == { @first => [], @second => [] } end it "should include modules even if they exist in multiple dirs in the modulepath" do modpath1 = File.join(@first, "foo") FileUtils.mkdir_p(modpath1) modpath2 = File.join(@second, "foo") FileUtils.mkdir_p(modpath2) env.modules_by_path.should == { @first => [Puppet::Module.new('foo', modpath1, env)], @second => [Puppet::Module.new('foo', modpath2, env)] } end it "should ignore modules with invalid names" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@first, 'foo2')) FileUtils.mkdir_p(File.join(@first, 'foo-bar')) FileUtils.mkdir_p(File.join(@first, 'foo_bar')) FileUtils.mkdir_p(File.join(@first, 'foo=bar')) FileUtils.mkdir_p(File.join(@first, 'foo bar')) FileUtils.mkdir_p(File.join(@first, 'foo.bar')) FileUtils.mkdir_p(File.join(@first, '-foo')) FileUtils.mkdir_p(File.join(@first, 'foo-')) FileUtils.mkdir_p(File.join(@first, 'foo--bar')) env.modules_by_path[@first].collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar} end end describe "#module_requirements" do it "should return a list of what modules depend on other modules" do PuppetSpec::Modules.create( 'foo', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => ">= 1.0.0" }] } ) PuppetSpec::Modules.create( 'bar', @second, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/foo', "version_requirement" => "<= 2.0.0" }] } ) PuppetSpec::Modules.create( 'baz', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "3.0.0" }] } ) PuppetSpec::Modules.create( 'alpha', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "~3.0.0" }] } ) env.module_requirements.should == { 'puppetlabs/alpha' => [], 'puppetlabs/foo' => [ { "name" => "puppetlabs/bar", "version" => "9.9.9", "version_requirement" => "<= 2.0.0" } ], 'puppetlabs/bar' => [ { "name" => "puppetlabs/alpha", "version" => "9.9.9", "version_requirement" => "~3.0.0" }, { "name" => "puppetlabs/baz", "version" => "9.9.9", "version_requirement" => "3.0.0" }, { "name" => "puppetlabs/foo", "version" => "9.9.9", "version_requirement" => ">= 1.0.0" } ], 'puppetlabs/baz' => [] } end end describe ".module_by_forge_name" do it "should find modules by forge_name" do mod = PuppetSpec::Modules.create( 'baz', @first, :metadata => {:author => 'puppetlabs'}, :environment => env ) env.module_by_forge_name('puppetlabs/baz').should == mod end it "should not find modules with same name by the wrong author" do mod = PuppetSpec::Modules.create( 'baz', @first, :metadata => {:author => 'sneakylabs'}, :environment => env ) env.module_by_forge_name('puppetlabs/baz').should == nil end it "should return nil when the module can't be found" do env.module_by_forge_name('ima/nothere').should be_nil end end describe ".modules" do it "should return an empty list if there are no modules" do env.modules.should == [] end it "should return a module named for every directory in each module path" do %w{foo bar}.each do |mod_name| FileUtils.mkdir_p(File.join(@first, mod_name)) end %w{bee baz}.each do |mod_name| FileUtils.mkdir_p(File.join(@second, mod_name)) end env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@second, 'foo')) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore modules with invalid names" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@first, 'foo2')) FileUtils.mkdir_p(File.join(@first, 'foo-bar')) FileUtils.mkdir_p(File.join(@first, 'foo_bar')) FileUtils.mkdir_p(File.join(@first, 'foo=bar')) FileUtils.mkdir_p(File.join(@first, 'foo bar')) env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar} end it "should create modules with the correct environment" do FileUtils.mkdir_p(File.join(@first, 'foo')) env.modules.each {|mod| mod.environment.should == env } end end end end describe "when performing initial import" do def parser_and_environment(name) env = Puppet::Node::Environment.new(name) parser = Puppet::Parser::ParserFactory.parser(env) Puppet::Parser::ParserFactory.stubs(:parser).returns(parser) [parser, env] end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet[:code] = "my code" parser, env = parser_and_environment('testing') parser.expects(:string=).with "my code" parser.expects(:parse) env.instance_eval { 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 filename = tmpfile('myfile') Puppet[:manifest] = filename parser, env = parser_and_environment('testing') parser.expects(:file=).with filename parser.expects(:parse) env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') Puppet[:code] = "" Puppet[:manifest] = filename parser, env = parser_and_environment('testing') parser.expects(:file=).with(filename).once parser.expects(:parse).once env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do Puppet::FileSystem.stubs(:exist?).returns true parser, env = parser_and_environment('testing') parser.expects(:file=).once parser.expects(:parse).raises ArgumentError expect do env.known_resource_types end.to raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do Puppet[:ignoreimport] = true parser, env = parser_and_environment('testing') parser.expects(:string=).never parser.expects(:file=).never parser.expects(:parse).never env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do parser, env = parser_and_environment('testing') parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") expect do env.known_resource_types end.to raise_error(Puppet::Error, /Syntax error at .../) env.known_resource_types.require_reparse?.should be_true end end end describe 'with classic parser' do before :each do Puppet[:parser] = 'current' end it_behaves_like 'the environment' end describe 'with future parser' do before :each do Puppet[:parser] = 'future' end it_behaves_like 'the environment' end describe '#current' do it 'should return the current context' do env = Puppet::Node::Environment.new(:test) Puppet::Context.any_instance.expects(:lookup).with(:current_environment).returns(env) Puppet.expects(:deprecation_warning).once Puppet::Node::Environment.current.should equal(env) end end end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 11f24729e..85ec6e127 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1,323 +1,323 @@ #! /usr/bin/env ruby require 'spec_helper' require 'matchers/json' describe Puppet::Node do include JSONMatchers - let(:environment) { Puppet::Node::Environment.create(:bar, [], '') } + let(:environment) { Puppet::Node::Environment.create(:bar, []) } let(:env_loader) { Puppet::Environments::Static.new(environment) } it "should register its document type as Node" do PSON.registered_document_types["Node"].should equal(Puppet::Node) end describe "when managing its environment" do it "should use any set environment" do Puppet.override(:environments => env_loader) do Puppet::Node.new("foo", :environment => "bar").environment.should == environment end end it "should support providing an actual environment instance" do Puppet::Node.new("foo", :environment => environment).environment.name.should == :bar end it "should determine its environment from its parameters if no environment is set" do Puppet.override(:environments => env_loader) do Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.should == environment end end it "should use the configured environment if no environment is provided" do Puppet[:environment] = environment.name.to_s Puppet.override(:environments => env_loader) do Puppet::Node.new("foo").environment.should == environment end end it "should allow the environment to be set after initialization" do node = Puppet::Node.new("foo") node.environment = :bar node.environment.name.should == :bar end it "should allow its environment to be set by parameters after initialization" do node = Puppet::Node.new("foo") node.parameters["environment"] = :bar node.environment.name.should == :bar end end it "can survive a round-trip through YAML" do facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b") node = Puppet::Node.new("hello", :environment => 'kjhgrg', :classes => ['erth', 'aiu'], :parameters => {"hostname"=>"food"} ) new_node = Puppet::Node.convert_from('yaml', node.render('yaml')) new_node.environment.should == node.environment new_node.parameters.should == node.parameters new_node.classes.should == node.classes new_node.name.should == node.name end it "can round-trip through pson" do facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b") node = Puppet::Node.new("hello", :environment => 'kjhgrg', :classes => ['erth', 'aiu'], :parameters => {"hostname"=>"food"} ) new_node = Puppet::Node.convert_from('pson', node.render('pson')) new_node.environment.should == node.environment new_node.parameters.should == node.parameters new_node.classes.should == node.classes new_node.name.should == node.name end it "validates against the node json schema", :unless => Puppet.features.microsoft_windows? do facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b") node = Puppet::Node.new("hello", :environment => 'kjhgrg', :classes => ['erth', 'aiu'], :parameters => {"hostname"=>"food"} ) expect(node.to_pson).to validate_against('api/schemas/node.json') end it "when missing optional parameters validates against the node json schema", :unless => Puppet.features.microsoft_windows? do facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b") node = Puppet::Node.new("hello", :environment => 'kjhgrg' ) expect(node.to_pson).to validate_against('api/schemas/node.json') end describe "when converting to json" do before do @node = Puppet::Node.new("mynode") end it "should provide its name" do @node.should set_json_attribute('name').to("mynode") end it "should produce a hash with the document_type set to 'Node'" do @node.should set_json_document_type_to("Node") end it "should include the classes if set" do @node.classes = %w{a b c} @node.should set_json_attribute("classes").to(%w{a b c}) end it "should not include the classes if there are none" do @node.should_not set_json_attribute('classes') end it "should include parameters if set" do @node.parameters = {"a" => "b", "c" => "d"} @node.should set_json_attribute('parameters').to({"a" => "b", "c" => "d"}) end it "should not include the parameters if there are none" do @node.should_not set_json_attribute('parameters') end it "should include the environment" do @node.environment = "production" @node.should set_json_attribute('environment').to('production') end end describe "when converting from json" do before do @node = Puppet::Node.new("mynode") @format = Puppet::Network::FormatHandler.format('pson') end def from_json(json) @format.intern(Puppet::Node, json) end it "should set its name" do Puppet::Node.should read_json_attribute('name').from(@node.to_pson).as("mynode") end it "should include the classes if set" do @node.classes = %w{a b c} Puppet::Node.should read_json_attribute('classes').from(@node.to_pson).as(%w{a b c}) end it "should include parameters if set" do @node.parameters = {"a" => "b", "c" => "d"} Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"}) end it "should include the environment" do Puppet.override(:environments => env_loader) do @node.environment = environment Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(environment) end end end end describe Puppet::Node, "when initializing" do before do @node = Puppet::Node.new("testnode") end it "should set the node name" do @node.name.should == "testnode" end it "should not allow nil node names" do proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) end it "should default to an empty parameter hash" do @node.parameters.should == {} end it "should default to an empty class array" do @node.classes.should == [] end it "should note its creation time" do @node.time.should be_instance_of(Time) end it "should accept parameters passed in during initialization" do params = {"a" => "b"} @node = Puppet::Node.new("testing", :parameters => params) @node.parameters.should == params end it "should accept classes passed in during initialization" do classes = %w{one two} @node = Puppet::Node.new("testing", :classes => classes) @node.classes.should == classes end it "should always return classes as an array" do @node = Puppet::Node.new("testing", :classes => "myclass") @node.classes.should == ["myclass"] end end describe Puppet::Node, "when merging facts" do before do @node = Puppet::Node.new("testnode") Puppet::Node::Facts.indirection.stubs(:find).with(@node.name, instance_of(Hash)).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) end it "should fail intelligently if it cannot find facts" do Puppet::Node::Facts.indirection.expects(:find).with(@node.name, instance_of(Hash)).raises "foo" lambda { @node.fact_merge }.should raise_error(Puppet::Error) end it "should prefer parameters already set on the node over facts from the node" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["one"].should == "a" end it "should add passed parameters to the parameter list" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["two"].should == "b" end it "should accept arbitrary parameters to merge into its parameters" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.merge "two" => "three" @node.parameters["two"].should == "three" end it "should add the environment to the list of parameters" do Puppet[:environment] = "one" @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "two" => "three" @node.parameters["environment"].should == "one" end it "should not set the environment if it is already set in the parameters" do Puppet[:environment] = "one" @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "environment" => "two" @node.parameters["environment"].should == "two" end end describe Puppet::Node, "when indirecting" do it "should default to the 'plain' node terminus" do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.terminus_class.should == :plain end end describe Puppet::Node, "when generating the list of names to search through" do before do @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) end it "should return an array of names" do @node.names.should be_instance_of(Array) end describe "and the node name is fully qualified" do it "should contain an entry for each part of the node name" do @node.names.should be_include("foo.domain.com") @node.names.should be_include("foo.domain") @node.names.should be_include("foo") end end it "should include the node's fqdn" do @node.names.should be_include("yay.domain.com") end it "should combine and include the node's hostname and domain if no fqdn is available" do @node.names.should be_include("yay.domain.com") end it "should contain an entry for each name available by stripping a segment of the fqdn" do @node.parameters["fqdn"] = "foo.deep.sub.domain.com" @node.names.should be_include("foo.deep.sub.domain") @node.names.should be_include("foo.deep.sub") end describe "and :node_name is set to 'cert'" do before do Puppet[:strict_hostname_checking] = false Puppet[:node_name] = "cert" end it "should use the passed-in key as the first value" do @node.names[0].should == "foo.domain.com" end describe "and strict hostname checking is enabled" do it "should only use the passed-in key" do Puppet[:strict_hostname_checking] = true @node.names.should == ["foo.domain.com"] end end end describe "and :node_name is set to 'facter'" do before do Puppet[:strict_hostname_checking] = false Puppet[:node_name] = "facter" end it "should use the node's 'hostname' fact as the first value" do @node.names[0].should == "yay" end end end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb index a5e40b2c3..5b2f7c14d 100755 --- a/spec/unit/parser/ast/collection_spec.rb +++ b/spec/unit/parser/ast/collection_spec.rb @@ -1,70 +1,70 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Parser::AST::Collection do before :each do @mytype = Puppet::Resource::Type.new(:definition, "mytype") - @environment = Puppet::Node::Environment.create(:testing, [], '') + @environment = Puppet::Node::Environment.create(:testing, []) @environment.known_resource_types.add @mytype @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode", :environment => @environment)) @scope = Puppet::Parser::Scope.new(@compiler) @overrides = stub_everything 'overrides' @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) end it "should evaluate its query" do query = mock 'query' collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual collection.type = 'mytype' query.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should instantiate a Collector for this type" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" @test_type = Puppet::Resource::Type.new(:definition, "test") @environment.known_resource_types.add @test_type Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) collection.evaluate(@scope) end it "should tell the compiler about this collector" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype" Puppet::Parser::Collector.stubs(:new).returns("whatever") @compiler.expects(:add_collection).with("whatever") collection.evaluate(@scope) end it "should evaluate overriden paramaters" do collector = stub_everything 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) @overrides.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should tell the collector about overrides" do collector = mock 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) collector.expects(:add_override) collection.evaluate(@scope) end it "should fail when evaluating undefined resource types" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "bogus" lambda { collection.evaluate(@scope) }.should raise_error "Resource type bogus doesn't exist" end end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index 8a6621520..ab1e40cf9 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -1,903 +1,903 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/compiler' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def [](attr) return nil if attr == :stage :main end def ref "#{type.to_s.capitalize}[#{title}]" end def evaluated? @evaluated end def builtin_type? @builtin end def virtual? @virtual end def class? false end def stage? false end def evaluate end def file "/fake/file/goes/here" end def line "42" end end describe Puppet::Parser::Compiler do include PuppetSpec::Files def resource(type, title) Puppet::Parser::Resource.new(type, title, :scope => @scope) end before :each do # Push me faster, I wanna go back in time! (Specifically, freeze time # across the test since we have a bunch of version == timestamp code # hidden away in the implementation and we keep losing the race.) # --daniel 2011-04-21 now = Time.now Time.stubs(:now).returns(now) - environment = Puppet::Node::Environment.create(:testing, [], '') + environment = Puppet::Node::Environment.create(:testing, []) @node = Puppet::Node.new("testnode", :facts => Puppet::Node::Facts.new("facts", {}), :environment => environment) @known_resource_types = environment.known_resource_types @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(@compiler, :source => stub('source')) @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) @scope.resource = @scope_resource 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::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 it "should clear the global caches before compile" do compiler = stub 'compiler' Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler catalog = stub 'catalog' compiler.expects(:compile).returns catalog catalog.expects(:to_resource) $known_resource_types = "rspec" $env_module_directories = "rspec" Puppet::Parser::Compiler.compile(@node) $known_resource_types = nil $env_module_directories = nil 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 =~ ['foo', 'bar'] end it "should transform node class hashes into a class list" do node = Puppet::Node.new("mynode") node.classes = {'foo'=>{'one'=>'p1'}, 'bar'=>{'two'=>'p2'}} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should add a 'main' stage to the catalog" do @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) 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 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, :evaluate_relationships] 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['a'].should == "b" @compiler.topscope['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 the main class if it exists" do compile_stub(:evaluate_main) main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(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::Resource::Type) end it "should add an edge between the main stage and main class" do @compiler.compile (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) @compiler.catalog.edge?(stage, klass).should be_true 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 = resource(:file, "testing") @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 = resource(:file, "testing") resource.stubs(:evaluated?).returns true @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 describe "when finishing" do before do @compiler.send(:evaluate_main) @catalog = @compiler.catalog end def add_resource(name, parent = nil) resource = Puppet::Parser::Resource.new "file", name, :scope => @scope @compiler.add_resource(@scope, resource) @catalog.add_edge(parent, resource) if parent resource end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf_resource) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = add_resource("finish1") resource1.expects(:finish).in_sequence(resources) resource2 = add_resource("finish2") resource2.expects(:finish).in_sequence(resources) @compiler.send(:finish) end it "should add each container's metaparams to its contained resources" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) @compiler.send(:finish) resource1[:noop].should be_true end it "should add metaparams recursively" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end it "should prefer metaparams from immediate parents" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) resource1[:noop] = false @compiler.send(:finish) resource2[:noop].should be_false end it "should merge tags downward" do main = @catalog.resource(:class, :main) main.tag("one") resource1 = add_resource("meh", main) resource1.tag "two" resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2.tags.should be_include("one") resource2.tags.should be_include("two") end it "should work if only middle resources have metaparams set" do main = @catalog.resource(:class, :main) resource1 = add_resource("meh", main) resource1[:noop] = true resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end end it "should return added resources in add order" do resource1 = resource(:file, "yay") @compiler.add_resource(@scope, resource1) resource2 = resource(:file, "youpi") @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = resource(: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 path = make_absolute("/foo") file1 = resource(:file, path) file2 = resource(:file, path) @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 = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should not add non-class resources that don't specify a stage to the 'main' stage" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:file, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should_not be_edge(main, resource) end it "should not add any parent-edges to stages" do stage = resource(:stage, "other") @compiler.add_resource(@scope, stage) @scope.resource = resource(:class, "foo") @compiler.catalog.edge?(@scope.resource, stage).should be_false end it "should not attempt to add stages to other stages" do other_stage = resource(:stage, "other") second_stage = resource(:stage, "second") @compiler.add_resource(@scope, other_stage) @compiler.add_resource(@scope, second_stage) second_stage[:stage] = "other" @compiler.catalog.edge?(other_stage, second_stage).should be_false end it "should have a method for looking up resources" do resource = resource(:yay, "foo") @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 = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = resource(:file, "testing") resource.virtual = true @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.compile 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, 'Failed to realize virtual resources something' 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, 'Failed to realize virtual resources one, two' end end describe "when evaluating relationships" do it "should evaluate each relationship with its catalog" do dep = stub 'dep' dep.expects(:evaluate).with(@compiler.catalog) @compiler.add_relationship dep @compiler.evaluate_relationships 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 raise an error if a class is not found" do @scope.expects(:find_hostclass).with("notfound", {:assume_fqname => false}).returns(nil) lambda{ @compiler.evaluate_classes(%w{notfound}, @scope) }.should raise_error(Puppet::Error, /Could not find class/) end it "should raise an error when it can't find class" do klasses = {'foo'=>nil} @node.classes = klasses @compiler.topscope.expects(:find_hostclass).with('foo', {:assume_fqname => false}).returns(nil) lambda{ @compiler.compile }.should raise_error(Puppet::Error, /Could not find class foo for testnode/) end end describe "when evaluating found classes" do before do Puppet.settings[:data_binding_terminus] = "none" @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass", {:assume_fqname => false}).returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:ensure_in_catalog).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end describe "and the classes are specified as a hash with parameters" do before do @node.classes = {} @ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') end # Define the given class with default parameters def define_class(name, parameters) @node.classes[name] = parameters klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => @ast_obj, 'p2' => @ast_obj}) @compiler.topscope.known_resource_types.add klass end def compile @catalog = @compiler.compile end it "should record which classes are evaluated" do classes = {'foo'=>{}, 'bar::foo'=>{}, 'bar'=>{}} classes.each { |c, params| define_class(c, params) } compile() classes.each { |name, p| @catalog.classes.should include(name) } end it "should provide default values for parameters that have no values specified" do define_class('foo', {}) compile() @catalog.resource(:class, 'foo')['p1'].should == "foo" end it "should use any provided values" do define_class('foo', {'p1' => 'real_value'}) compile() @catalog.resource(:class, 'foo')['p1'].should == "real_value" end it "should support providing some but not all values" do define_class('foo', {'p1' => 'real_value'}) compile() @catalog.resource(:class, 'Foo')['p1'].should == "real_value" @catalog.resource(:class, 'Foo')['p2'].should == "foo" end it "should ensure each node class is in catalog and has appropriate tags" do klasses = ['bar::foo'] @node.classes = klasses ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') klasses.each do |name| klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => ast_obj, 'p2' => ast_obj}) @compiler.topscope.known_resource_types.add klass end catalog = @compiler.compile r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } r2.tags.should == Puppet::Util::TagSet.new(['bar::foo', 'class', 'bar', 'foo']) end end it "should fail if required parameters are missing" do klass = {'foo'=>{'a'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'a' => nil, 'b' => nil}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Must pass b to Class[Foo]") end it "should fail if invalid parameters are passed" do klass = {'foo'=>{'3'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Invalid parameter 3") end it "should ensure class is in catalog without params" do @node.classes = klasses = {'foo'=>nil} foo = Puppet::Resource::Type.new(:hostclass, 'foo') @compiler.topscope.known_resource_types.add foo catalog = @compiler.compile catalog.classes.should include 'foo' end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:ensure_in_catalog).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(:ensure_in_catalog).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",{:assume_fqname => false}).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 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(:ensure_in_catalog).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(:ensure_in_catalog).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(:ensure_in_catalog).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end end describe "when evaluating node classes" do include PuppetSpec::Compiler describe "when provided classes in array format" do let(:node) { Puppet::Node.new('someone', :classes => ['something']) } describe "when the class exists" do it "should succeed if the class is already included" do manifest = <<-MANIFEST class something {} include something MANIFEST catalog = compile_to_catalog(manifest, node) catalog.resource('Class', 'Something').should_not be_nil end it "should evaluate the class without parameters if it's not already included" do manifest = "class something {}" catalog = compile_to_catalog(manifest, node) catalog.resource('Class', 'Something').should_not be_nil end end it "should fail if the class doesn't exist" do expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/) end end describe "when provided classes in hash format" do describe "for classes without parameters" do let(:node) { Puppet::Node.new('someone', :classes => {'something' => {}}) } describe "when the class exists" do it "should succeed if the class is already included" do manifest = <<-MANIFEST class something {} include something MANIFEST catalog = compile_to_catalog(manifest, node) catalog.resource('Class', 'Something').should_not be_nil end it "should evaluate the class if it's not already included" do manifest = <<-MANIFEST class something {} MANIFEST catalog = compile_to_catalog(manifest, node) catalog.resource('Class', 'Something').should_not be_nil end end it "should fail if the class doesn't exist" do expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/) end end describe "for classes with parameters" do let(:node) { Puppet::Node.new('someone', :classes => {'something' => {'configuron' => 'defrabulated'}}) } describe "when the class exists" do it "should fail if the class is already included" do manifest = <<-MANIFEST class something($configuron=frabulated) {} include something MANIFEST expect { compile_to_catalog(manifest, node) }.to raise_error(Puppet::Error, /Class\[Something\] is already declared/) end it "should evaluate the class if it's not already included" do manifest = <<-MANIFEST class something($configuron=frabulated) {} MANIFEST catalog = compile_to_catalog(manifest, node) resource = catalog.resource('Class', 'Something') resource['configuron'].should == 'defrabulated' end end it "should fail if the class doesn't exist" do expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/) end end end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "File[/foo]", :type => "my" @resource = resource(:file, "/foo") 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, 'Could not find resource(s) File[/foo] for overriding' end end end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index 02ef3d5cf..3eb43b276 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -1,149 +1,149 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/parser/files' describe Puppet::Parser::Files do include PuppetSpec::Files - let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } + let(:environment) { Puppet::Node::Environment.create(:testing, []) } before do @basepath = make_absolute("/somepath") end describe "when searching for templates" do it "should return fully-qualified templates directly" do Puppet::Parser::Files.expects(:modulepath).never Puppet::Parser::Files.find_template(@basepath + "/my/template", environment).should == @basepath + "/my/template" end it "should return the template from the first found module" do mod = mock 'module' mod.expects(:template).returns("/one/mymod/templates/mytemplate") environment.expects(:module).with("mymod").returns mod Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/one/mymod/templates/mytemplate" end it "should return the file in the templatedir if it exists" do Puppet[:templatedir] = "/my/templates" Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) Puppet::FileSystem.stubs(:exist?).returns(true) Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == File.join(Puppet[:templatedir], "mymod/mytemplate") end it "should not raise an error if no valid templatedir exists and the template exists in a module" do mod = mock 'module' mod.expects(:template).returns("/one/mymod/templates/mytemplate") environment.expects(:module).with("mymod").returns mod Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(nil) Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/one/mymod/templates/mytemplate" end it "should return unqualified templates if they exist in the template dir" do Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate", environment).should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns true Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate", environment).should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do Puppet::FileSystem.expects(:exist?).with("/my/templates/mytemplate").returns false Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate", environment).should be_nil end it "should accept relative templatedirs" do Puppet::FileSystem.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" File.expects(:directory?).with(File.expand_path("my/templates")).returns(true) Puppet::Parser::Files.find_template("mytemplate", environment).should == File.expand_path("my/templates/mytemplate") end it "should use the environment templatedir if no module is found and an environment is specified" do Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/myenv/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do Puppet::FileSystem.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(environment).returns(["/myenv/templates", "/two/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", environment).should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate", environment).should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) Puppet::FileSystem.expects(:exist?).with("/one/templates/mytemplate").returns(false) Puppet::FileSystem.expects(:exist?).with("/two/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate", environment).should == "/two/templates/mytemplate" end it "should use the node environment if specified" do mod = mock 'module' environment.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") Puppet::Parser::Files.find_template("mymod/envtemplate", environment).should == "/my/modules/mymod/templates/envtemplate" end it "should return nil if no template can be found" do Puppet::Parser::Files.find_template("foomod/envtemplate", environment).should be_nil end end describe "when searching for manifests" do it "should ignore invalid modules" do mod = mock 'module' environment.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") Puppet.expects(:value).with(:modulepath).never Dir.stubs(:glob).returns %w{foo} Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", environment) end end describe "when searching for manifests in a module" do def a_module_in_environment(env, name) mod = Puppet::Module.new(name, "/one/#{name}", env) env.stubs(:module).with(name).returns mod mod.stubs(:match_manifests).with("init.pp").returns(["/one/#{name}/manifests/init.pp"]) end it "returns no files when no module is found" do module_name, files = Puppet::Parser::Files.find_manifests_in_modules("not_here_module/foo", environment) expect(files).to be_empty expect(module_name).to be_nil end it "should return the name of the module and the manifests from the first found module" do a_module_in_environment(environment, "mymod") Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", environment).should == ["mymod", ["/one/mymod/manifests/init.pp"]] end it "does not find the module when it is a different environment" do - different_env = Puppet::Node::Environment.create(:different, [], '') + different_env = Puppet::Node::Environment.create(:different, []) a_module_in_environment(environment, "mymod") Puppet::Parser::Files.find_manifests_in_modules("mymod/init.pp", different_env).should_not include("mymod") end end end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index 91013586c..b3c04d1af 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -1,135 +1,135 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Parser::Functions do def callable_functions_from(mod) Class.new { include mod }.new end let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) } - let(:environment) { Puppet::Node::Environment.create(:myenv, [], '') } + let(:environment) { Puppet::Node::Environment.create(:myenv, []) } before do Puppet::Parser::Functions.reset end it "should have a method for returning an environment-specific module" do Puppet::Parser::Functions.environment_module(environment).should be_instance_of(Module) end describe "when calling newfunction" do it "should create the function in the environment module" do Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } function_module.should be_method_defined :function_name end it "should warn if the function already exists" do Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } Puppet.expects(:warning) Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } end it "should raise an error if the function type is not correct" do expect { Puppet::Parser::Functions.newfunction("name", :type => :unknown) { |args| } }.to raise_error Puppet::DevError, "Invalid statement type :unknown" end it "instruments the function to profile the execution" do messages = [] Puppet::Util::Profiler.current = Puppet::Util::Profiler::WallClock.new(proc { |msg| messages << msg }, "id") Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } callable_functions_from(function_module).function_name([]) messages.first.should =~ /Called name/ end end describe "when calling function to test function existence" do it "should return false if the function doesn't exist" do Puppet::Parser::Functions.autoloader.stubs(:load) Puppet::Parser::Functions.function("name").should be_false end it "should return its name if the function exists" do Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| } Puppet::Parser::Functions.function("name").should == "function_name" end it "should try to autoload the function if it doesn't exist yet" do Puppet::Parser::Functions.autoloader.expects(:load) Puppet::Parser::Functions.function("name") end it "combines functions from the root with those from the current environment" do Puppet.override(:current_environment => Puppet.lookup(:root_environment)) do Puppet::Parser::Functions.newfunction("onlyroot", :type => :rvalue) do |args| end end - Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [], '')) do + Puppet.override(:current_environment => Puppet::Node::Environment.create(:other, [])) do Puppet::Parser::Functions.newfunction("other_env", :type => :rvalue) do |args| end expect(Puppet::Parser::Functions.function("onlyroot")).to eq("function_onlyroot") expect(Puppet::Parser::Functions.function("other_env")).to eq("function_other_env") end expect(Puppet::Parser::Functions.function("other_env")).to be_false end end describe "when calling function to test arity" do let(:function_module) { Module.new } before do Puppet::Parser::Functions.stubs(:environment_module).returns(function_module) end it "should raise an error if the function is called with too many arguments" do Puppet::Parser::Functions.newfunction("name", :arity => 2) { |args| } expect { callable_functions_from(function_module).function_name([1,2,3]) }.to raise_error ArgumentError end it "should raise an error if the function is called with too few arguments" do Puppet::Parser::Functions.newfunction("name", :arity => 2) { |args| } expect { callable_functions_from(function_module).function_name([1]) }.to raise_error ArgumentError end it "should not raise an error if the function is called with correct number of arguments" do Puppet::Parser::Functions.newfunction("name", :arity => 2) { |args| } expect { callable_functions_from(function_module).function_name([1,2]) }.to_not raise_error end it "should raise an error if the variable arg function is called with too few arguments" do Puppet::Parser::Functions.newfunction("name", :arity => -3) { |args| } expect { callable_functions_from(function_module).function_name([1]) }.to raise_error ArgumentError end it "should not raise an error if the variable arg function is called with correct number of arguments" do Puppet::Parser::Functions.newfunction("name", :arity => -3) { |args| } expect { callable_functions_from(function_module).function_name([1,2]) }.to_not raise_error end it "should not raise an error if the variable arg function is called with more number of arguments" do Puppet::Parser::Functions.newfunction("name", :arity => -3) { |args| } expect { callable_functions_from(function_module).function_name([1,2,3]) }.to_not raise_error end end describe "::arity" do it "returns the given arity of a function" do Puppet::Parser::Functions.newfunction("name", :arity => 4) { |args| } Puppet::Parser::Functions.arity(:name).should == 4 end it "returns -1 if no arity is given" do Puppet::Parser::Functions.newfunction("name") { |args| } Puppet::Parser::Functions.arity(:name).should == -1 end end end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 56062d47e..eb9370292 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -1,535 +1,535 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Parser do Puppet::Parser::AST before :each do @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 expect { Puppet::Parser::Parser.new }.to raise_error(ArgumentError, /wrong number of arguments/) end it "should set the environment" do - env = Puppet::Node::Environment.create(:testing, [], '') + env = Puppet::Node::Environment.create(:testing, []) Puppet::Parser::Parser.new(env).environment.should == env end it "should be able to look up the environment-specific resource type collection" do - env = Puppet::Node::Environment.create(:development, [], '') + env = Puppet::Node::Environment.create(:development, []) rtc = env.known_resource_types parser = Puppet::Parser::Parser.new env parser.known_resource_types.should equal(rtc) end context "when importing" do it "uses the directory of the currently parsed file" do @parser.lexer.stubs(:file).returns "/tmp/current_file" @parser.known_resource_types.loader.expects(:import).with("newfile", "/tmp") @parser.import("newfile") end it "uses the current working directory, when there is no file being parsed" do @parser.known_resource_types.loader.expects(:import).with('one', Dir.pwd) @parser.known_resource_types.loader.expects(:import).with('two', Dir.pwd) @parser.parse("import 'one', 'two'") end end describe "when parsing files" do before do Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileSystem.stubs(:read).returns "" @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 end describe "when parsing append operator" do it "should not raise syntax errors" do expect { @parser.parse("$var += something") }.to_not raise_error end it "should raise syntax error on incomplete syntax " do expect { @parser.parse("$var += ") }.to raise_error(Puppet::ParseError, /Syntax error at end of file/) end it "should create ast::VarDef with append=true" do vardef = @parser.parse("$var += 2").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end it "should work with arrays too" do vardef = @parser.parse("$var += ['test']").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end end describe "when parsing selector" do it "should support hash access on the left hand side" do expect { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.to_not raise_error end end describe "parsing 'unless'" do it "should create the correct ast objects" do Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } @parser.parse("unless false { $var = 1 }") end it "should not raise an error with empty statements" do expect { @parser.parse("unless false { }") }.to_not raise_error end #test for bug #13296 it "should not override 'unless' as a parameter inside resources" do lambda { @parser.parse("exec {'/bin/echo foo': unless => '/usr/bin/false',}") }.should_not raise_error end end describe "when parsing parameter names" do Puppet::Parser::Lexer::KEYWORDS.sort_tokens.each do |keyword| it "should allow #{keyword} as a keyword" do lambda { @parser.parse("exec {'/bin/echo foo': #{keyword} => '/usr/bin/false',}") }.should_not raise_error end end end describe "when parsing 'if'" do it "not, it should create the correct ast objects" do Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } @parser.parse("if ! true { $var = 1 }") end it "boolean operation, it should create the correct ast objects" do Puppet::Parser::AST::BooleanOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Boolean) and h[:lval].is_a?(Puppet::Parser::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 Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:rval].is_a?(Puppet::Parser::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' Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]==">" }.returns(aststub) Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="==" }.returns(aststub) Puppet::Parser::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 expect { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.to raise_error(Puppet::ParseError, /Syntax error at '\)'/) end end describe "when parsing resource references" do it "should not raise syntax errors" do expect { @parser.parse('exec { test: param => File["a"] }') }.to_not raise_error end it "should not raise syntax errors with multiple references" do expect { @parser.parse('exec { test: param => File["a","b"] }') }.to_not raise_error end it "should create an ast::ResourceReference" do Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg| arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(Puppet::Parser::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 expect { @parser.parse('Resource["title"] { param => value }') }.to_not raise_error end it "should not raise syntax errors with multiple overrides" do expect { @parser.parse('Resource["title1","title2"] { param => value }') }.to_not raise_error end it "should create an ast::ResourceOverride" do #Puppet::Parser::AST::ResourceOverride.expects(:new).with { |arg| # arg[:line]==1 and arg[:object].is_a?(Puppet::Parser::AST::ResourceReference) and arg[:parameters].is_a?(Puppet::Parser::AST::ResourceParam) #} ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0] ro.should be_a(Puppet::Parser::AST::ResourceOverride) ro.line.should == 1 ro.object.should be_a(Puppet::Parser::AST::ResourceReference) ro.parameters[0].should be_a(Puppet::Parser::AST::ResourceParam) end end describe "when parsing if statements" do it "should not raise errors with empty if" do expect { @parser.parse("if true { }") }.to_not raise_error end it "should not raise errors with empty else" do expect { @parser.parse("if false { notice('if') } else { }") }.to_not raise_error end it "should not raise errors with empty if and else" do expect { @parser.parse("if false { } else { }") }.to_not raise_error end it "should create a nop node for empty branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { }") end it "should create a nop node for empty else branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { notice('test') } else { }") end it "should build a chain of 'ifs' if there's an 'elsif'" do expect { @parser.parse(<<-PP) }.to_not raise_error if true { notice('test') } elsif true {} else { } PP end end describe "when parsing function calls" do it "should not raise errors with no arguments" do expect { @parser.parse("tag()") }.to_not raise_error end it "should not raise errors with rvalue function with no args" do expect { @parser.parse("$a = template()") }.to_not raise_error end it "should not raise errors with arguments" do expect { @parser.parse("notice(1)") }.to_not raise_error end it "should not raise errors with multiple arguments" do expect { @parser.parse("notice(1,2)") }.to_not raise_error end it "should not raise errors with multiple arguments and a trailing comma" do expect { @parser.parse("notice(1,2,)") }.to_not raise_error end end describe "when parsing arrays" do it "should parse an array" do expect { @parser.parse("$a = [1,2]") }.to_not raise_error end it "should not raise errors with a trailing comma" do expect { @parser.parse("$a = [1,2,]") }.to_not raise_error end it "should accept an empty array" do expect { @parser.parse("$var = []\n") }.to_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 = Puppet::Resource::Type.new(:hostclass, "myclass", :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" } @lexer.expects(:file).returns "/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{ |docs, line| docs == true }.returns({}) @parser.ast(@class, :file => "/bar") end it "should get docs from lexer using the correct AST line number" do @class.expects(:use_docs).returns true @class.stubs(:new).with{ |a| a[:doc] == "doc" } @lexer.expects(:getcomment).with(12).returns "doc" @parser.ast(@class, :file => "/bar", :line => 12) 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 use the known resource types to check for them by name" do @parser.known_resource_types.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 use the known resource types to check for them by name" do @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:hostclass,{}).returns(:this_value) @parser.find_hostclass("namespace","name").should == :this_value end end describe "when parsing classes" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should not create new classes" do @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass) @krt.hostclass("foobar").should be_nil end it "should correctly set the parent class when one is provided" do @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness" end it "should correctly set the parent class for multiple classes at a time" do statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code statements[0].instantiate('')[0].parent.should == "yayness" statements[1].instantiate('')[0].parent.should == "bar" end it "should define the code when some is provided" do @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil end it "should accept parameters with trailing comma" do @parser.parse("file { '/example': ensure => file, }").should be end it "should accept parametrized classes with trailing comma" do @parser.parse("class foobar ($var1 = 0,) { $var = val }").code[0].code.should_not be_nil end it "should define parameters when provided" do foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0] foobar.arguments.should == {"biz" => nil, "baz" => nil} end end describe "when parsing resources" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should be able to parse class resources" do @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) expect { @parser.parse("class { foobar: biz => stuff }") }.to_not raise_error end it "should correctly mark exported resources as exported" do @parser.parse("@@file { '/file': }").code[0].exported.should be_true end it "should correctly mark virtual resources as virtual" do @parser.parse("@file { '/file': }").code[0].virtual.should be_true end end describe "when parsing nodes" do it "should be able to parse a node with a single name" do node = @parser.parse("node foo { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 1 node.names[0].value.should == "foo" end it "should be able to parse a node with two names" do node = @parser.parse("node foo, bar { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 2 node.names[0].value.should == "foo" node.names[1].value.should == "bar" end it "should be able to parse a node with three names" do node = @parser.parse("node foo, bar, baz { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 3 node.names[0].value.should == "foo" node.names[1].value.should == "bar" node.names[2].value.should == "baz" end end it "should fail if trying to collect defaults" do expect { @parser.parse("@Port { protocols => tcp }") }.to raise_error(Puppet::ParseError, /Defaults are not virtualizable/) end context "when parsing collections" do it "should parse basic collections" do @parser.parse("Port <| |>").code. should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } end it "should parse fully qualified collections" do @parser.parse("Port::Range <| |>").code. should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } end end it "should not assign to a fully qualified variable" do expect { @parser.parse("$one::two = yay") }.to raise_error(Puppet::ParseError, /Cannot assign to variables in other namespaces/) end it "should parse assignment of undef" do tree = @parser.parse("$var = undef") tree.code.children[0].should be_an_instance_of Puppet::Parser::AST::VarDef tree.code.children[0].value.should be_an_instance_of Puppet::Parser::AST::Undef end context "#namesplit" do { "base::sub" => %w{base sub}, "main" => ["", "main"], "one::two::three::four" => ["one::two::three", "four"], }.each do |input, output| it "should split #{input.inspect} to #{output.inspect}" do @parser.namesplit(input).should == output end end end it "should treat classes as case insensitive" do @parser.known_resource_types.import_ast(@parser.parse("class yayness {}"), '') @parser.known_resource_types.hostclass('yayness'). should == @parser.find_hostclass("", "YayNess") end it "should treat defines as case insensitive" do @parser.known_resource_types.import_ast(@parser.parse("define funtest {}"), '') @parser.known_resource_types.hostclass('funtest'). should == @parser.find_hostclass("", "fUntEst") end context "deprecations" do it "should flag use of import as deprecated" do Puppet.expects(:deprecation_warning).once @parser.known_resource_types.loader.expects(:import).with('foo', Dir.pwd) @parser.parse("import 'foo'") end end end diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb index f78f83982..d0ec5f49c 100755 --- a/spec/unit/parser/resource_spec.rb +++ b/spec/unit/parser/resource_spec.rb @@ -1,586 +1,586 @@ require 'spec_helper' describe Puppet::Parser::Resource do before do - environment = Puppet::Node::Environment.create(:testing, [], '') + environment = Puppet::Node::Environment.create(:testing, []) @node = Puppet::Node.new("yaynode", :environment => environment) @known_resource_types = environment.known_resource_types @compiler = Puppet::Parser::Compiler.new(@node) @source = newclass "" @scope = @compiler.topscope end def mkresource(args = {}) args[:source] ||= @source args[:scope] ||= @scope params = args[:parameters] || {:one => "yay", :three => "rah"} if args[:parameters] == :none args.delete(:parameters) elsif not args[:parameters].is_a? Array args[:parameters] = paramify(args[:source], params) end Puppet::Parser::Resource.new("resource", "testing", 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::Resource::Type.new(:hostclass, name) end def newdefine(name) @known_resource_types.add Puppet::Resource::Type.new(:definition, name) end def newnode(name) @known_resource_types.add Puppet::Resource::Type.new(:node, name) end it "should get its environment from its scope" do scope = stub 'scope', :source => stub("source"), :namespaces => nil scope.expects(:environment).returns("foo").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => scope).environment.should == "foo" end it "should use the resource type collection helper module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should use the scope's environment as its environment" do @scope.expects(:environment).returns("myenv").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => @scope).environment.should == "myenv" 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("file", "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("file", "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("whatever", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should have an 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(:copy_as_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("resource", "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("resource", "testing", :source => @source, :scope => @scope)[:title].should == "testing" end describe "when initializing" do before do @arguments = {:scope => @scope} end it "should fail unless #{name.to_s} is specified" do expect { Puppet::Parser::Resource.new('file', '/my/file') }.to raise_error(ArgumentError, /Resources require a hash as last argument/) end it "should set the reference correctly" do res = Puppet::Parser::Resource.new("resource", "testing", @arguments) res.ref.should == "Resource[testing]" end it "should be tagged with user tags" do tags = [ "tag1", "tag2" ] @arguments[:parameters] = [ param(:tag, tags , :source) ] res = Puppet::Parser::Resource.new("resource", "testing", @arguments) res.should be_tagged("tag1") res.should be_tagged("tag2") end end describe "when evaluating" do before do @catalog = Puppet::Resource::Catalog.new source = stub('source') source.stubs(:module_name) @scope = Puppet::Parser::Scope.new(@compiler, :source => source) @catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => @scope)) end it "should evaluate the associated AST definition" do definition = newdefine "mydefine" res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source, :catalog => @catalog) definition.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST class" do @class = newclass "myclass" res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source, :catalog => @catalog) @class.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST node" do nodedef = newnode("mynode") res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source, :catalog => @catalog) nodedef.expects(:evaluate_code).with(res) res.evaluate end it "should add an edge to any specified stage for class resources" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) other_stage = Puppet::Parser::Resource.new(:stage, "other", :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, other_stage) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'other' @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.edge?(other_stage, resource).should be_true end it "should fail if an unknown stage is specified" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'other' expect { resource.evaluate }.to raise_error(ArgumentError, /Could not find stage other specified by/) end it "should add edges from the class resources to the parent's stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) foo_stage = Puppet::Parser::Resource.new(:stage, :foo_stage, :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, foo_stage) @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'foo_stage' @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.should be_edge(foo_stage, resource) end it "should allow edges to propagate multiple levels down the scope hierarchy" do Puppet[:code] = <<-MANIFEST stage { before: before => Stage[main] } class alpha { include beta } class beta { include gamma } class gamma { } class { alpha: stage => before } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone') # Stringify them to make for easier lookup edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]} edges.should include(["Stage[before]", "Class[Alpha]"]) edges.should include(["Stage[before]", "Class[Beta]"]) edges.should include(["Stage[before]", "Class[Gamma]"]) end it "should use the specified stage even if the parent scope specifies one" do Puppet[:code] = <<-MANIFEST stage { before: before => Stage[main], } stage { after: require => Stage[main], } class alpha { class { beta: stage => after } } class beta { } class { alpha: stage => before } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone') edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]} edges.should include(["Stage[before]", "Class[Alpha]"]) edges.should include(["Stage[after]", "Class[Beta]"]) end it "should add edges from top-level class resources to the main stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.should be_edge(main, resource) end end describe "when finishing" do before do @class = newclass "myclass" @nodedef = newnode("mynode") @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source) end it "should do nothing if it has already been finished" do @resource.finish @resource.expects(:add_defaults).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 end describe "when being tagged" do before do @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} @scope.stubs(:resource).returns @scope_resource @resource = Puppet::Parser::Resource.new("file", "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("file", "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("one::two", "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("file", "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 expect { @resource.tag("bad*tag") }.to raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do expect { @resource.tag("-badtag") }.to raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do expect { @resource.tag("bad tag") }.to raise_error(Puppet::ParseError) end it "should allow alpha tags" do expect { @resource.tag("good_tag") }.to_not raise_error 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) expect { @resource.merge(@override) }.to 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(:parameters).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(:parameters).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 not merge parameter values when multiple resources are overriden with '+>' at once " do @resource_2 = mkresource :source => @source @resource. set_parameter(:testing, "old_val_1") @resource_2.set_parameter(:testing, "old_val_2") @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "new_val", :source => @resource.source) param.add = true @override.set_parameter(param) @resource. merge(@override) @resource_2.merge(@override) @resource [:testing].should == %w{old_val_1 new_val} @resource_2[:testing].should == %w{old_val_2 new_val} 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(:copy_as_resource) end describe "when being converted to a resource" do before do @parser_resource = mkresource :scope => @scope, :parameters => {:foo => "bar", :fee => "fum"} end it "should create an instance of Puppet::Resource" do @parser_resource.copy_as_resource.should be_instance_of(Puppet::Resource) end it "should set the type correctly on the Puppet::Resource" do @parser_resource.copy_as_resource.type.should == @parser_resource.type end it "should set the title correctly on the Puppet::Resource" do @parser_resource.copy_as_resource.title.should == @parser_resource.title end it "should copy over all of the parameters" do result = @parser_resource.copy_as_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.copy_as_resource.tags.should == @parser_resource.tags end it "should copy over the line" do @parser_resource.line = 40 @parser_resource.copy_as_resource.line.should == 40 end it "should copy over the file" do @parser_resource.file = "/my/file" @parser_resource.copy_as_resource.file.should == "/my/file" end it "should copy over the 'exported' value" do @parser_resource.exported = true @parser_resource.copy_as_resource.exported.should be_true end it "should copy over the 'virtual' value" do @parser_resource.virtual = true @parser_resource.copy_as_resource.virtual.should be_true end it "should convert any parser resource references to Puppet::Resource instances" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref} result = @parser_resource.copy_as_resource result[:fee].should == Puppet::Resource.new(:file, "/my/file") end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]} result = @parser_resource.copy_as_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")] end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper" do ref1 = Puppet::Resource.new("file", "/my/file1") ref2 = Puppet::Resource.new("file", "/my/file2") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]} result = @parser_resource.copy_as_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")] end it "should fail if the same param is declared twice" do lambda do @parser_resource = mkresource :source => @source, :parameters => [ 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 describe "when validating" do it "should check each parameter" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource[:three] = :four resource.expects(:validate_parameter).with(:one) resource.expects(:validate_parameter).with(:three) resource.send(:validate) end it "should raise a parse error when there's a failure" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource.expects(:validate_parameter).with(:one).raises ArgumentError expect { resource.send(:validate) }.to raise_error(Puppet::ParseError) end end describe "when setting parameters" do before do @source = newclass "foobar" @resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => @source end it "should accept Param instances and add them to the parameter list" do param = Puppet::Parser::Resource::Param.new :name => "foo", :value => "bar", :source => @source @resource.set_parameter(param) @resource["foo"].should == "bar" end it "should fail when provided a parameter name but no value" do expect { @resource.set_parameter("myparam") }.to raise_error(ArgumentError) end it "should allow parameters to be set to 'false'" do @resource.set_parameter("myparam", false) @resource["myparam"].should be_false end it "should use its source when provided a parameter name and value" do @resource.set_parameter("myparam", "myvalue") @resource["myparam"].should == "myvalue" end end # part of #629 -- the undef keyword. Make sure 'undef' params get skipped. it "should not include 'undef' parameters when converting itself to a hash" do resource = Puppet::Parser::Resource.new "file", "/tmp/testing", :source => mock("source"), :scope => mock("scope") resource[:owner] = :undef resource[:mode] = "755" resource.to_hash[:owner].should be_nil end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index b9b10618f..f14faf831 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -1,661 +1,661 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/compiler' require 'puppet_spec/scope' describe Puppet::Parser::Scope do include PuppetSpec::Scope before :each do @scope = Puppet::Parser::Scope.new( Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) ) @scope.source = Puppet::Resource::Type.new(:node, :foo) @topscope = @scope.compiler.topscope @scope.parent = @topscope end describe "create_test_scope_for_node" do let(:node_name) { "node_name_foo" } let(:scope) { create_test_scope_for_node(node_name) } it "should be a kind of Scope" do scope.should be_a_kind_of(Puppet::Parser::Scope) end it "should set the source to a node resource" do scope.source.should be_a_kind_of(Puppet::Resource::Type) end it "should have a compiler" do scope.compiler.should be_a_kind_of(Puppet::Parser::Compiler) end it "should set the parent to the compiler topscope" do scope.parent.should be(scope.compiler.topscope) end end it "should return a scope for use in a test harness" do create_test_scope_for_node("node_name_foo").should be_a_kind_of(Puppet::Parser::Scope) 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 it "should be able to retrieve its parent module name from the source of its parent type" do @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => "foo") @scope.parent_module_name.should == "foo" end it "should return a nil parent module name if it has no parent" do @topscope.parent_module_name.should be_nil end it "should return a nil parent module name if its parent has no source" do @scope.parent_module_name.should be_nil end it "should get its environment from its compiler" do - env = Puppet::Node::Environment.create(:testing, [], '') + env = Puppet::Node::Environment.create(:testing, []) compiler = stub 'compiler', :environment => env, :is_a? => true scope = Puppet::Parser::Scope.new(compiler) scope.environment.should equal(env) end it "should fail if no compiler is supplied" do expect { Puppet::Parser::Scope.new }.to raise_error(ArgumentError, /wrong number of arguments/) end it "should fail if something that isn't a compiler is supplied" do expect { Puppet::Parser::Scope.new(:compiler => true) }.to raise_error(Puppet::DevError, /you must pass a compiler instance/) end it "should use the resource type collection helper to find its known resource types" do Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) end describe "when custom functions are called" do - let(:env) { Puppet::Node::Environment.create(:testing, [], '') } + let(:env) { Puppet::Node::Environment.create(:testing, []) } let(:compiler) { Puppet::Parser::Compiler.new(Puppet::Node.new('foo', :environment => env)) } let(:scope) { Puppet::Parser::Scope.new(compiler) } it "calls methods prefixed with function_ as custom functions" do scope.function_sprintf(["%b", 123]).should == "1111011" end it "raises an error when arguments are not passed in an Array" do expect do scope.function_sprintf("%b", 123) end.to raise_error ArgumentError, /custom functions must be called with a single array that contains the arguments/ end it "raises an error on subsequent calls when arguments are not passed in an Array" do scope.function_sprintf(["first call"]) expect do scope.function_sprintf("%b", 123) end.to raise_error ArgumentError, /custom functions must be called with a single array that contains the arguments/ end it "raises NoMethodError when the not prefixed" do expect { scope.sprintf(["%b", 123]) }.to raise_error(NoMethodError) end it "raises NoMethodError when prefixed with function_ but it doesn't exist" do expect { scope.function_fake_bs(['cows']) }.to raise_error(NoMethodError) end end describe "when initializing" do it "should extend itself with its environment's Functions module as well as the default" do - env = Puppet::Node::Environment.create(:myenv, [], '') + env = Puppet::Node::Environment.create(:myenv, []) root = Puppet.lookup(:root_environment) compiler = stub 'compiler', :environment => env, :is_a? => true scope = Puppet::Parser::Scope.new(compiler) scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(env)) scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(root)) end it "should extend itself with the default Functions module if its environment is the default" do root = Puppet.lookup(:root_environment) node = Puppet::Node.new('localhost') compiler = Puppet::Parser::Compiler.new(node) scope = Puppet::Parser::Scope.new(compiler) scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(root)) end end describe "when looking up a variable" do it "should support :lookupvar and :setvar for backward compatibility" do @scope.setvar("var", "yep") @scope.lookupvar("var").should == "yep" end it "should fail if invoked with a non-string name" do expect { @scope[:foo] }.to raise_error(Puppet::ParseError, /Scope variable name .* not a string/) expect { @scope[:foo] = 12 }.to raise_error(Puppet::ParseError, /Scope variable name .* not a string/) end it "should return nil for unset variables" do @scope["var"].should be_nil end it "should be able to look up values" do @scope["var"] = "yep" @scope["var"].should == "yep" end it "should be able to look up hashes" do @scope["var"] = {"a" => "b"} @scope["var"].should == {"a" => "b"} end it "should be able to look up variables in parent scopes" do @topscope["var"] = "parentval" @scope["var"].should == "parentval" end it "should prefer its own values to parent values" do @topscope["var"] = "parentval" @scope["var"] = "childval" @scope["var"].should == "childval" end it "should be able to detect when variables are set" do @scope["var"] = "childval" @scope.should be_include("var") end it "does not allow changing a set value" do @scope["var"] = "childval" expect { @scope["var"] = "change" }.to raise_error(Puppet::Error, "Cannot reassign variable var") end it "should be able to detect when variables are not set" do @scope.should_not be_include("var") end describe "and the variable is qualified" do before :each do @known_resource_types = @scope.known_resource_types node = Puppet::Node.new('localhost') @compiler = Puppet::Parser::Compiler.new(node) end def newclass(name) @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def create_class_scope(name) klass = newclass(name) catalog = Puppet::Resource::Catalog.new catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => Puppet::Parser::Scope.new(@compiler))) Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source'), :catalog => catalog).evaluate @scope.class_scope(klass) end it "should be able to look up explicitly fully qualified variables from main" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("") other_scope["othervar"] = "otherval" @scope["::othervar"].should == "otherval" end it "should be able to look up explicitly fully qualified variables from other scopes" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("other") other_scope["var"] = "otherval" @scope["::other::var"].should == "otherval" end it "should be able to look up deeply qualified variables" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("other::deep::klass") other_scope["var"] = "otherval" @scope["other::deep::klass::var"].should == "otherval" end it "should return nil for qualified variables that cannot be found in other classes" do other_scope = create_class_scope("other::deep::klass") @scope["other::deep::klass::var"].should be_nil end it "should warn and return nil for qualified variables whose classes have not been evaluated" do klass = newclass("other::deep::klass") @scope.expects(:warning) @scope["other::deep::klass::var"].should be_nil end it "should warn and return nil for qualified variables whose classes do not exist" do @scope.expects(:warning) @scope["other::deep::klass::var"].should be_nil end it "should return nil when asked for a non-string qualified variable from a class that does not exist" do @scope.stubs(:warning) @scope["other::deep::klass::var"].should be_nil end it "should return nil 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["other::deep::klass::var"].should be_nil end end context "and strict_variables is true" do before(:each) do Puppet[:strict_variables] = true end it "should raise an error when unknown variable is looked up" do expect { @scope['john_doe'] }.to raise_error(/Undefined variable/) end it "should raise an error when unknown qualified variable is looked up" do expect { @scope['nowhere::john_doe'] }.to raise_error(/Undefined variable/) end end end describe "when variables are set with append=true" do it "should raise error if the variable is already defined in this scope" do @scope.setvar("var", "1", :append => false) expect { @scope.setvar("var", "1", :append => true) }.to raise_error( Puppet::ParseError, "Cannot append, variable var is defined in this scope" ) end it "should lookup current variable value" do @scope.expects(:[]).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["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["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["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) expect { @scope.setvar("var", "not a hash", :append => true) }.to raise_error( ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" ) 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.set_match_data({1 => :value}) @scope["1"].should == :value end it "should remove the variable value when unset_ephemeral_var(:all) is called" do # @scope.setvar("1", :value, :ephemeral => true) @scope.set_match_data({1 => :value}) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var(:all) @scope["1"].should be_nil end it "should not remove classic variables when unset_ephemeral_var(:all) is called" do @scope['myvar'] = :value1 @scope.set_match_data({1 => :value2}) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var(:all) @scope["myvar"].should == :value1 end it "should raise an error when setting numerical variable" do expect { @scope.setvar("1", :value3, :ephemeral => true) }.to raise_error(Puppet::ParseError, /Cannot assign to a numeric match result variable/) end describe "with more than one level" do it "should prefer latest ephemeral scopes" do @scope.set_match_data({0 => :earliest}) @scope.new_ephemeral @scope.set_match_data({0 => :latest}) @scope["0"].should == :latest end it "should be able to report the current level" do @scope.ephemeral_level.should == 1 @scope.new_ephemeral @scope.ephemeral_level.should == 2 end it "should not check presence of an ephemeral variable accross multiple levels" do # This test was testing that scope actuallys screwed up - making values from earlier matches show as if they # where true for latest match - insanity ! @scope.new_ephemeral @scope.set_match_data({1 => :value1}) @scope.new_ephemeral @scope.set_match_data({0 => :value2}) @scope.new_ephemeral @scope.include?("1").should be_false end it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do @scope.new_ephemeral @scope.set_match_data({1 => :value1}) @scope.new_ephemeral @scope.set_match_data({0 => :value2}) @scope.new_ephemeral @scope.include?("2").should be_false end it "should not get ephemeral values from earlier scope when not in later" do @scope.set_match_data({1 => :value1}) @scope.new_ephemeral @scope.set_match_data({0 => :value2}) @scope.include?("1").should be_false end describe "when calling unset_ephemeral_var with a level" do it "should remove ephemeral scopes up to this level" do @scope.set_match_data({1 => :value1}) @scope.new_ephemeral @scope.set_match_data({1 => :value2}) level = @scope.ephemeral_level() @scope.new_ephemeral @scope.set_match_data({1 => :value3}) @scope.unset_ephemeral_var(level) @scope["1"].should == :value2 end end end end context "when using ephemeral as local scope" do it "should store all variables in local scope" do @scope.new_ephemeral true @scope.setvar("apple", :fruit) @scope["apple"].should == :fruit end it "should remove all local scope variables on unset" do @scope.new_ephemeral true @scope.setvar("apple", :fruit) @scope["apple"].should == :fruit @scope.unset_ephemeral_var @scope["apple"].should == nil end it "should be created from a hash" do @scope.ephemeral_from({ "apple" => :fruit, "strawberry" => :berry}) @scope["apple"].should == :fruit @scope["strawberry"].should == :berry 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 expect { @scope.ephemeral_from("match") }.to raise_error(ArgumentError, /Invalid regex match data/) end it "should set $0 with the full match" do # This is an internal impl detail test @scope.expects(:new_match_scope).with { |*arg| arg[0][0] == "this is a string" } @scope.ephemeral_from(@match) end it "should set every capture as ephemeral var" do # This is an internal impl detail test @match.stubs(:[]).with(1).returns(:capture1) @match.stubs(:[]).with(2).returns(:capture2) @scope.expects(:new_match_scope).with { |*arg| arg[0][1] == :capture1 && arg[0][2] == :capture2 } @scope.ephemeral_from(@match) end it "should shadow previous match variables" do # This is an internal impl detail test @match.stubs(:[]).with(1).returns(:capture1) @match.stubs(:[]).with(2).returns(:capture2) @match2 = stub 'match', :is_a? => true @match2.stubs(:[]).with(1).returns(:capture2_1) @match2.stubs(:[]).with(2).returns(nil) @scope.ephemeral_from(@match) @scope.ephemeral_from(@match2) @scope.lookupvar('2').should == nil end it "should create a new ephemeral level" do level_before = @scope.ephemeral_level @scope.ephemeral_from(@match) expect(level_before < @scope.ephemeral_level) end end it "should use its namespaces to find hostclasses" do klass = @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "a::b::c") @scope.add_namespace "a::b" @scope.find_hostclass("c").should equal(klass) end it "should use its namespaces to find definitions" do define = @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "a::b::c") @scope.add_namespace "a::b" @scope.find_definition("c").should equal(define) end describe "when managing defaults" do it "should be able to set and lookup defaults" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param) @scope.lookupdefaults(:mytype).should == {:myparam => param} end it "should fail if a default is already defined and a new default is being defined" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param) expect { @scope.define_settings(:mytype, param) }.to raise_error(Puppet::ParseError, /Default already defined .* cannot redefine/) end it "should return multiple defaults at once" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param1) param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param2) @scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end it "should look up defaults defined in parent scopes" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param1) child_scope = @scope.newscope param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) child_scope.define_settings(:mytype, param2) child_scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end end context "#true?" do { "a string" => true, "true" => true, "false" => true, true => true, "" => false, :undef => false, nil => false }.each do |input, output| it "should treat #{input.inspect} as #{output}" do Puppet::Parser::Scope.true?(input).should == output end end end context "when producing a hash of all variables (as used in templates)" do it "should contain all defined variables in the scope" do @scope.setvar("orange", :tangerine) @scope.setvar("pear", :green) @scope.to_hash.should == {'orange' => :tangerine, 'pear' => :green } end it "should contain variables in all local scopes (#21508)" do @scope.new_ephemeral true @scope.setvar("orange", :tangerine) @scope.setvar("pear", :green) @scope.new_ephemeral true @scope.setvar("apple", :red) @scope.to_hash.should == {'orange' => :tangerine, 'pear' => :green, 'apple' => :red } end it "should contain all defined variables in the scope and all local scopes" do @scope.setvar("orange", :tangerine) @scope.setvar("pear", :green) @scope.new_ephemeral true @scope.setvar("apple", :red) @scope.to_hash.should == {'orange' => :tangerine, 'pear' => :green, 'apple' => :red } end it "should not contain varaibles in match scopes (non local emphemeral)" do @scope.new_ephemeral true @scope.setvar("orange", :tangerine) @scope.setvar("pear", :green) @scope.ephemeral_from(/(f)(o)(o)/.match('foo')) @scope.to_hash.should == {'orange' => :tangerine, 'pear' => :green } end it "should delete values that are :undef in inner scope" do @scope.new_ephemeral true @scope.setvar("orange", :tangerine) @scope.setvar("pear", :green) @scope.new_ephemeral true @scope.setvar("apple", :red) @scope.setvar("orange", :undef) @scope.to_hash.should == {'pear' => :green, 'apple' => :red } end end end diff --git a/spec/unit/rails/host_spec.rb b/spec/unit/rails/host_spec.rb index 746c3a075..59f7e64d1 100755 --- a/spec/unit/rails/host_spec.rb +++ b/spec/unit/rails/host_spec.rb @@ -1,162 +1,162 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/node/environment' describe "Puppet::Rails::Host", :if => can_use_scratch_database? do before do require 'puppet/rails/host' setup_scratch_database @node = Puppet::Node.new("foo") @node.environment = "production" @node.ipaddress = "127.0.0.1" @host = stub 'host', :environment= => nil, :ip= => nil end describe "when converting a Puppet::Node instance into a Rails instance" do it "should modify any existing instance in the database" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should create a new instance in the database if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:new).with(:name => "foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should copy the environment from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.environment = "production" @host.expects(:environment=).with {|x| x.name.to_s == 'production' } Puppet::Rails::Host.from_puppet(@node) end it "should stringify the environment" do host = Puppet::Rails::Host.new - host.environment = Puppet::Node::Environment.create(:production, [], '') + host.environment = Puppet::Node::Environment.create(:production, []) host.environment.class.should == String end it "should copy the ipaddress from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.ipaddress = "192.168.0.1" @host.expects(:ip=).with "192.168.0.1" Puppet::Rails::Host.from_puppet(@node) end it "should not save the Rails instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @host.expects(:save).never Puppet::Rails::Host.from_puppet(@node) end end describe "when converting a Puppet::Rails::Host instance into a Puppet::Node instance" do before do @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @node = Puppet::Node.new("foo") Puppet::Node.stubs(:new).with("foo").returns @node end it "should create a new instance with the correct name" do Puppet::Node.expects(:new).with("foo").returns @node @host.to_puppet end it "should copy the environment from the Rails instance" do @host.environment = "prod" @node.expects(:environment=).with "prod" @host.to_puppet end it "should copy the ipaddress from the Rails instance" do @host.ip = "192.168.0.1" @node.expects(:ipaddress=).with "192.168.0.1" @host.to_puppet end end describe "when merging catalog resources and database resources" do before :each do Puppet[:thin_storeconfigs] = false @resource1 = stub_everything 'res1' @resource2 = stub_everything 'res2' @resources = [ @resource1, @resource2 ] @dbresource1 = stub_everything 'dbres1' @dbresource2 = stub_everything 'dbres2' @dbresources = { 1 => @dbresource1, 2 => @dbresource2 } @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:find_resources).returns(@dbresources) @host.stubs(:find_resources_parameters_tags) @host.stubs(:compare_to_catalog) @host.stubs(:id).returns(1) end it "should find all database resources" do @host.expects(:find_resources) @host.merge_resources(@resources) end it "should find all paramaters and tags for those database resources" do @host.expects(:find_resources_parameters_tags).with(@dbresources) @host.merge_resources(@resources) end it "should compare all database resources to catalog" do @host.expects(:compare_to_catalog).with(@dbresources, @resources) @host.merge_resources(@resources) end it "should compare only exported resources in thin_storeconfigs mode" do Puppet[:thin_storeconfigs] = true @resource1.stubs(:exported?).returns(true) @host.expects(:compare_to_catalog).with(@dbresources, [ @resource1 ]) @host.merge_resources(@resources) end end describe "when searching the database for host resources" do before :each do Puppet[:thin_storeconfigs] = false @resource1 = stub_everything 'res1', :id => 1 @resource2 = stub_everything 'res2', :id => 2 @resources = [ @resource1, @resource2 ] @dbresources = stub 'resources' @dbresources.stubs(:find).returns(@resources) @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:resources).returns(@dbresources) end it "should return a hash keyed by id of all resources" do @host.find_resources.should == { 1 => @resource1, 2 => @resource2 } end it "should return a hash keyed by id of only exported resources in thin_storeconfigs mode" do Puppet[:thin_storeconfigs] = true @dbresources.expects(:find).with { |*h| h[1][:conditions] == { :exported => true } }.returns([]) @host.find_resources end end end diff --git a/spec/unit/resource/type_collection_spec.rb b/spec/unit/resource/type_collection_spec.rb index 4cd5b339e..ad54c7ca1 100755 --- a/spec/unit/resource/type_collection_spec.rb +++ b/spec/unit/resource/type_collection_spec.rb @@ -1,417 +1,417 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/resource/type' describe Puppet::Resource::TypeCollection do include PuppetSpec::Files - let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } + let(:environment) { Puppet::Node::Environment.create(:testing, []) } before do @instance = Puppet::Resource::Type.new(:hostclass, "foo") @code = Puppet::Resource::TypeCollection.new(environment) end it "should consider '<<' to be an alias to 'add' but should return self" do @code.expects(:add).with "foo" @code.expects(:add).with "bar" @code << "foo" << "bar" end it "should set itself as the code collection for added resource types" do node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) node.resource_type_collection.should equal(@code) end it "should store node resource types as nodes" do node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) end it "should fail if a duplicate node is added" do @code.add(Puppet::Resource::Type.new(:node, "foo")) expect do @code.add(Puppet::Resource::Type.new(:node, "foo")) end.to raise_error(Puppet::ParseError, /cannot redefine/) end it "should store hostclasses as hostclasses" do klass = Puppet::Resource::Type.new(:hostclass, "foo") @code.add(klass) @code.hostclass("foo").should equal(klass) end it "merge together hostclasses of the same name" do klass1 = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "first") klass2 = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "second") @code.add(klass1) @code.add(klass2) @code.hostclass("foo").doc.should == "firstsecond" end it "should store definitions as definitions" do define = Puppet::Resource::Type.new(:definition, "foo") @code.add(define) @code.definition("foo").should equal(define) end it "should fail if a duplicate definition is added" do @code.add(Puppet::Resource::Type.new(:definition, "foo")) expect do @code.add(Puppet::Resource::Type.new(:definition, "foo")) end.to raise_error(Puppet::ParseError, /cannot be redefined/) end it "should remove all nodes, classes, and definitions when cleared" do loader = Puppet::Resource::TypeCollection.new(environment) loader.add Puppet::Resource::Type.new(:hostclass, "class") loader.add Puppet::Resource::Type.new(:definition, "define") loader.add Puppet::Resource::Type.new(:node, "node") watched_file = tmpfile('watched_file') loader.watch_file(watched_file) loader.clear loader.hostclass("class").should be_nil loader.definition("define").should be_nil loader.node("node").should be_nil loader.should_not be_watching_file(watched_file) end describe "when resolving namespaces" do [ ['', '::foo', ['foo']], ['a', '::foo', ['foo']], ['a::b', '::foo', ['foo']], [['a::b'], '::foo', ['foo']], [['a::b', 'c'], '::foo', ['foo']], [['A::B', 'C'], '::Foo', ['foo']], ['', '', ['']], ['a', '', ['']], ['a::b', '', ['']], [['a::b'], '', ['']], [['a::b', 'c'], '', ['']], [['A::B', 'C'], '', ['']], ['', 'foo', ['foo']], ['a', 'foo', ['a::foo', 'foo']], ['a::b', 'foo', ['a::b::foo', 'a::foo', 'foo']], ['A::B', 'Foo', ['a::b::foo', 'a::foo', 'foo']], [['a::b'], 'foo', ['a::b::foo', 'a::foo', 'foo']], [['a', 'b'], 'foo', ['a::foo', 'foo', 'b::foo']], [['a::b', 'c::d'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'c::d::foo', 'c::foo']], [['a::b', 'a::c'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'a::c::foo']], ].each do |namespaces, name, expected_result| it "should resolve #{name.inspect} in namespaces #{namespaces.inspect} correctly" do @code.instance_eval { resolve_namespaces(namespaces, name) }.should == expected_result end end end describe "when looking up names" do before do @type = Puppet::Resource::Type.new(:hostclass, "ns::klass") end it "should support looking up with multiple namespaces" do @code.add @type @code.find_hostclass(%w{boo baz ns}, "klass").should equal(@type) end it "should not attempt to import anything when the type is already defined" do @code.add @type @code.loader.expects(:import).never @code.find_hostclass(%w{ns}, "klass").should equal(@type) end describe "that need to be loaded" do it "should use the loader to load the files" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass(["ns"], "klass") end it "should downcase the name and downcase and array-fy the namespaces before passing to the loader" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass("Ns", "Klass") end it "should use the class returned by the loader" do @code.loader.expects(:try_load_fqname).returns(:klass) @code.expects(:hostclass).with("ns::klass").returns(false) @code.find_hostclass("ns", "klass").should == :klass end it "should return nil if the name isn't found" do @code.loader.stubs(:try_load_fqname).returns(nil) @code.find_hostclass("Ns", "Klass").should be_nil end it "already-loaded names at broader scopes should not shadow autoloaded names" do @code.add Puppet::Resource::Type.new(:hostclass, "bar") @code.loader.expects(:try_load_fqname).with(:hostclass, "foo::bar").returns(:foobar) @code.find_hostclass("foo", "bar").should == :foobar end it "should not try to autoload names that we couldn't autoload in a previous step if ignoremissingtypes is enabled" do Puppet[:ignoremissingtypes] = true @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass").returns(nil) @code.loader.expects(:try_load_fqname).with(:hostclass, "klass").returns(nil) @code.find_hostclass("Ns", "Klass").should be_nil Puppet.expects(:debug).at_least_once.with {|msg| msg =~ /Not attempting to load hostclass/} @code.find_hostclass("Ns", "Klass").should be_nil end end end %w{hostclass node definition}.each do |data| describe "behavior of add for #{data}" do it "should return the added #{data}" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(data, "foo") loader.add(instance).should equal(instance) end it "should retrieve #{data} insensitive to case" do loader = Puppet::Resource::TypeCollection.new(environment) 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::Resource::TypeCollection.new(environment).send(data, "foo").should be_nil end 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::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("namespace", "::foo::bar").should equal(instance) end it "should return nil if the instance name is fully qualified and no such instance exists" do loader = Puppet::Resource::TypeCollection.new(environment) loader.find_hostclass("namespace", "::foo::bar").should be_nil end it "should be able to find classes in the base namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo") loader.add instance loader.find_hostclass("", "foo").should equal(instance) end it "should return the partially qualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo", "bar::baz").should equal(instance) end it "should be able to find partially qualified objects in any of the provided namespaces" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass(["nons", "foo", "otherns"], "bar::baz").should equal(instance) end it "should return the unqualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo", "bar").should equal(instance) end it "should return the unqualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo::bar::baz", "bar").should equal(instance) end it "should should return the partially qualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "bar::baz").should equal(instance) end it "should return the qualified object if it exists in the root namespace" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "foo::bar::baz").should equal(instance) end it "should return nil if the object cannot be found" do loader = Puppet::Resource::TypeCollection.new(environment) instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "eh").should be_nil end describe "when topscope has a class that has the same name as a local class" do before do @loader = Puppet::Resource::TypeCollection.new(environment) [ "foo::bar", "bar" ].each do |name| @loader.add Puppet::Resource::Type.new(:hostclass, name) end end it "should favor the local class, if the name is unqualified" do @loader.find_hostclass("foo", "bar").name.should == 'foo::bar' end it "should only look in the topclass, if the name is qualified" do @loader.find_hostclass("foo", "::bar").name.should == 'bar' end it "should only look in the topclass, if we assume the name is fully qualified" do @loader.find_hostclass("foo", "bar", :assume_fqname => true).name.should == 'bar' end end it "should not look in the local scope for classes when the name is qualified" do @loader = Puppet::Resource::TypeCollection.new(environment) @loader.add Puppet::Resource::Type.new(:hostclass, "foo::bar") @loader.find_hostclass("foo", "::bar").should == nil end end it "should be able to find nodes" do node = Puppet::Resource::Type.new(:node, "bar") loader = Puppet::Resource::TypeCollection.new(environment) loader.add(node) loader.find_node(stub("ignored"), "bar").should == node end it "should indicate whether any nodes are defined" do loader = Puppet::Resource::TypeCollection.new(environment) loader.add_node(Puppet::Resource::Type.new(:node, "foo")) loader.should be_nodes end it "should indicate whether no nodes are defined" do Puppet::Resource::TypeCollection.new(environment).should_not be_nodes end describe "when finding nodes" do before :each do @loader = Puppet::Resource::TypeCollection.new(environment) end it "should return any node whose name exactly matches the provided node name" do 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::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::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::Resource::TypeCollection.new(environment) Puppet::Util::WatchedFile.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 WatchedFile to watch files" do Puppet::Util::WatchedFile.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::WatchedFile.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::WatchedFile.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 determining the configuration version" do before do @code = Puppet::Resource::TypeCollection.new(environment) 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::Execution.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::Execution.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) lambda { @code.version }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index b3a4aab78..497f0e711 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -1,777 +1,777 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/resource/type' require 'matchers/json' describe Puppet::Resource::Type do include JSONMatchers it "should have a 'name' attribute" do Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" end [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr| it "should have a '#{attr}' attribute" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.send(attr.to_s + "=", "yay") type.send(attr).should == "yay" end end [:hostclass, :node, :definition].each do |type| it "should know when it is a #{type}" do Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true end end it "should indirect 'resource_type'" do Puppet::Resource::Type.indirection.name.should == :resource_type end it "should default to 'parser' for its terminus class" do Puppet::Resource::Type.indirection.terminus_class.should == :parser end describe "when converting to json" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo") end def from_json(json) Puppet::Resource::Type.from_data_hash(json) end def double_convert Puppet::Resource::Type.from_data_hash(PSON.parse(@type.to_pson)) end it "should include the name and type" do double_convert.name.should == @type.name double_convert.type.should == @type.type end it "should validate with only name and kind" do expect(@type.to_pson).to validate_against('api/schemas/resource_type.json') end it "should validate with all fields set" do @type.set_arguments("one" => nil, "two" => "foo") @type.line = 100 @type.doc = "A weird type" @type.file = "/etc/manifests/thing.pp" @type.parent = "one::two" expect(@type.to_pson).to validate_against('api/schemas/resource_type.json') end it "should include any arguments" do @type.set_arguments("one" => nil, "two" => "foo") double_convert.arguments.should == {"one" => nil, "two" => "foo"} end it "should not include arguments if none are present" do @type.to_pson["arguments"].should be_nil end [:line, :doc, :file, :parent].each do |attr| it "should include #{attr} when set" do @type.send(attr.to_s + "=", "value") double_convert.send(attr).should == "value" end it "should not include #{attr} when not set" do @type.to_pson[attr.to_s].should be_nil end end it "should not include docs if they are empty" do @type.doc = "" @type.to_pson["doc"].should be_nil end end describe "when a node" do it "should allow a regex as its name" do lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error end it "should allow an 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::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::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::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::Resource::Type.new(:node, /w/).should be_name_is_regex end it "should set its namespace to ''" do 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::Resource::Type.new(:node, /ww/).name.should == "ww" end it "should downcase the regex when returning the name as a string" do 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::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::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::Resource::Type.new(:node, /.ww/).should respond_to(:match) end it "should return true when its regex matches the provided name" do Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true end it "should return true when its regex matches the provided name" do 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::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::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::Resource::Type.new(:node, "foo").match("bar").should be_false end it "should match names insensitive to case" do Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true end end end describe "when initializing" do it "should require a resource super type" do Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass end it "should fail if provided an invalid resource super type" do 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::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" end it "should set its namespace to the downcased, stringified qualified name for classes" do Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz" end [:definition, :node].each do |type| it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" end end %w{code line file doc}.each do |arg| it "should set #{arg} if provided" do 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::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) type.should be_valid_parameter("foo") type.should be_valid_parameter("baz") end it "should set any provided arguments with they keys as strings" do type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) type.should be_valid_parameter(:foo) type.should be_valid_parameter(:baz) end it "should function if provided no arguments" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.should_not be_valid_parameter(: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::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo") end it "should return true if it is a metaparam" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require") end it "should return true if the parameter is named 'name'" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name") end it "should return false if it is not a metaparam and was not provided at initialization" do Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness") end end describe "when setting its parameters in the scope" do before do @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo")), :source => stub("source")) @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope) @type = Puppet::Resource::Type.new(:definition, "foo") @resource.environment.known_resource_types.add @type end ['module_name', 'name', 'title'].each do |variable| it "should allow #{variable} to be evaluated as param default" do @type.instance_eval { @module_name = "bar" } var = Puppet::Parser::AST::Variable.new({'value' => variable}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == 'bar' end end # this test is to clarify a crazy edge case # if you specify these special names as params, the resource # will override the special variables it "should allow the resource to override defaults" do @type.set_arguments :name => nil @resource[:name] = 'foobar' var = Puppet::Parser::AST::Variable.new({'value' => 'name'}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == 'foobar' end it "should set each of the resource's parameters as variables in the scope" do @type.set_arguments :foo => nil, :boo => nil @resource[:foo] = "bar" @resource[:boo] = "baz" @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "bar" @scope['boo'].should == "baz" end it "should set the variables as strings" do @type.set_arguments :foo => nil @resource[:foo] = "bar" @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "bar" end it "should fail if any of the resource's parameters are not valid attributes" do @type.set_arguments :foo => nil @resource[: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 => Puppet::Parser::AST::String.new(:value => "something") @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "something" end it "should set all default values as parameters in the resource" do @type.set_arguments :foo => Puppet::Parser::AST::String.new(:value => "something") @type.set_resource_parameters(@resource, @scope) @resource[:foo].should == "something" end it "should fail if the resource does not provide a value for a required argument" do @type.set_arguments :foo => nil 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 @type.set_resource_parameters(@resource, @scope) @scope['title'].should == "bar" end it "should set the resource's name as a variable if not otherwise provided" do @type.set_resource_parameters(@resource, @scope) @scope['name'].should == "bar" end it "should set its module name in the scope if available" do @type.instance_eval { @module_name = "mymod" } @type.set_resource_parameters(@resource, @scope) @scope["module_name"].should == "mymod" end it "should set its caller module name in the scope if available" do @scope.expects(:parent_module_name).returns "mycaller" @type.set_resource_parameters(@resource, @scope) @scope["caller_module_name"].should == "mycaller" end end describe "when describing and managing parent classes" do before do - environment = Puppet::Node::Environment.create(:testing, [], '') + environment = Puppet::Node::Environment.create(:testing, []) @krt = environment.known_resource_types @parent = Puppet::Resource::Type.new(:hostclass, "bar") @krt.add @parent @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") @krt.add @child @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo", :environment => environment))) end it "should be able to define a parent" do 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(@scope).should equal(@parent) end it "should be able to find parent nodes" do parent = Puppet::Resource::Type.new(:node, "bar") @krt.add parent child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") @krt.add child child.parent_type(@scope).should equal(parent) end it "should cache a reference to the parent type" do @krt.stubs(:hostclass).with("foo::bar").returns nil @krt.expects(:hostclass).with("bar").once.returns @parent @child.parent_type(@scope) @child.parent_type end it "should correctly state when it is another type's child" do @child.parent_type(@scope) @child.should be_child_of(@parent) end it "should be considered the child of a parent's parent" do @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") @krt.add @grandchild @child.parent_type(@scope) @grandchild.parent_type(@scope) @grandchild.should be_child_of(@parent) end it "should correctly state when it is not another type's child" do @notchild = Puppet::Resource::Type.new(:hostclass, "baz") @krt.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 @resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope) # This is so the internal resource lookup works, yo. @compiler.catalog.add_resource @resource @type = Puppet::Resource::Type.new(:hostclass, "foo") @resource.environment.known_resource_types.add @type end it "should add node regex captures to its scope" do @type = Puppet::Resource::Type.new(:node, /f(\w)o(.*)$/) match = @type.match('foo') code = stub 'code' @type.stubs(:code).returns code subscope = stub 'subscope', :compiler => @compiler @scope.expects(:newscope).with(:source => @type, :namespace => '', :resource => @resource).returns subscope elevel = 876 subscope.expects(:ephemeral_level).returns elevel subscope.expects(:ephemeral_from).with(match, nil, nil).returns subscope code.expects(:safeevaluate).with(subscope) subscope.expects(:unset_ephemeral_var).with(elevel) # Just to keep the stub quiet about intermediate calls @type.expects(:set_resource_parameters).with(@resource, subscope) @type.evaluate_code(@resource) end it "should add hostclass names to the classes list" do @type.evaluate_code(@resource) @compiler.catalog.classes.should be_include("foo") end it "should not add defined resource names to the classes list" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @compiler.catalog.classes.should_not be_include("foo") end it "should set all of its parameters in a subscope" do subscope = stub 'subscope', :compiler => @compiler @scope.expects(:newscope).with(:source => @type, :namespace => 'foo', :resource => @resource).returns subscope @type.expects(:set_resource_parameters).with(@resource, subscope) @type.evaluate_code(@resource) end it "should not create a subscope for the :main class" do @resource.stubs(:title).returns(:main) @type.expects(:subscope).never @type.expects(:set_resource_parameters).with(@resource, @scope) @type.evaluate_code(@resource) end it "should store the class scope" do @type.evaluate_code(@resource) @scope.class_scope(@type).should be_instance_of(@scope.class) end it "should still create a scope but not store it if the type is a definition" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @scope.class_scope(@type).should be_nil end it "should evaluate the AST code if any is provided" do code = stub 'code' @type.stubs(:code).returns code subscope = stub_everything("subscope", :compiler => @compiler) @scope.stubs(:newscope).returns subscope code.expects(:safeevaluate).with subscope @type.evaluate_code(@resource) end describe "and ruby code is provided" do it "should create a DSL Resource API and evaluate it" do @type.stubs(:ruby_code).returns(proc { "foo" }) @api = stub 'api' Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api @api.expects(:evaluate) @type.evaluate_code(@resource) end end it "should noop if there is no code" do @type.expects(:code).returns nil @type.evaluate_code(@resource) end describe "and it has a parent class" do before do @parent_type = Puppet::Resource::Type.new(:hostclass, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add @parent_type end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end describe "and it has a parent node" do before do @type = Puppet::Resource::Type.new(:node, "foo") @parent_type = Puppet::Resource::Type.new(:node, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add(@parent_type) end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end end describe "when creating a resource" do before do @node = Puppet::Node.new("foo", :environment => 'env') @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(@compiler) @top = Puppet::Resource::Type.new :hostclass, "top" @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" @code = Puppet::Resource::TypeCollection.new("env") @code.add @top @code.add @middle @node.environment.stubs(:known_resource_types).returns(@code) end it "should create a resource instance" do @top.ensure_in_catalog(@scope).should be_instance_of(Puppet::Parser::Resource) end it "should set its resource type to 'class' when it is a hostclass" do Puppet::Resource::Type.new(:hostclass, "top").ensure_in_catalog(@scope).type.should == "Class" end it "should set its resource type to 'node' when it is a node" do Puppet::Resource::Type.new(:node, "top").ensure_in_catalog(@scope).type.should == "Node" end it "should fail when it is a definition" do lambda { Puppet::Resource::Type.new(:definition, "top").ensure_in_catalog(@scope) }.should raise_error(ArgumentError) end it "should add the created resource to the scope's catalog" do @top.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should add specified parameters to the resource" do @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'}) @compiler.catalog.resource(:class, "top")['one'].should == '1' @compiler.catalog.resource(:class, "top")['two'].should == '2' end it "should not require params for a param class" do @top.ensure_in_catalog(@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.ensure_in_catalog(@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.ensure_in_catalog(@scope, {}) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should fail if you try to create duplicate class resources" do othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope ) # add the same class resource to the catalog @compiler.catalog.add_resource(othertop) lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should fail to evaluate if a parent class is defined but cannot be found" do othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" @code.add othertop lambda { othertop.ensure_in_catalog(@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.ensure_in_catalog(@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.ensure_in_catalog(@scope).should == "something" end it "should not create a new parent resource if one already exists and it has a parent class" do @top.ensure_in_catalog(@scope) top_resource = @compiler.catalog.resource(:class, "top") @middle.ensure_in_catalog(@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.ensure_in_catalog(@scope) @compiler.catalog.should be_tagged("middle") end it "should tag the catalog with the parent class tags when it is evaluated" do @middle.ensure_in_catalog(@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::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error) end it "should fail unless the source instance is a class" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:node, "foo") lambda { dest.merge(source) }.should raise_error(Puppet::Error) end it "should fail if both classes have different parent classes" do code = Puppet::Resource::TypeCollection.new("env") {"a" => "b", "c" => "d"}.each do |parent, child| 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(Puppet::Error) end it "should fail if it's named 'main' and 'freeze_main' is enabled" do Puppet.settings[:freeze_main] = true code = Puppet::Resource::TypeCollection.new("env") code.add Puppet::Resource::Type.new(:hostclass, "") other = Puppet::Resource::Type.new(:hostclass, "") lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error) end it "should copy the other class's parent if it has not parent" do dest = Puppet::Resource::Type.new(:hostclass, "bar") 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::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::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 set the other class's code as its code if it has none" do 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::BlockExpression.new(:children => [code("dest")]) dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) scode = Puppet::Parser::AST::BlockExpression.new(:children => [code("source")]) 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/resource_spec.rb b/spec/unit/resource_spec.rb index 42d0a7ec8..73c27c1fe 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -1,979 +1,979 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/resource' describe Puppet::Resource do include PuppetSpec::Files let(:basepath) { make_absolute("/somepath") } - let(:environment) { Puppet::Node::Environment.create(:testing, [], '') } + let(:environment) { Puppet::Node::Environment.create(:testing, []) } [:catalog, :file, :line].each do |attr| it "should have an #{attr} attribute" do resource = Puppet::Resource.new("file", "/my/file") resource.should respond_to(attr) resource.should respond_to(attr.to_s + "=") end end it "should have a :title attribute" do Puppet::Resource.new(:user, "foo").title.should == "foo" end it "should require the type and title" do expect { Puppet::Resource.new }.to raise_error(ArgumentError) end it "should canonize types to capitalized strings" do Puppet::Resource.new(:user, "foo").type.should == "User" end it "should canonize qualified types so all strings are capitalized" do Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" end it "should tag itself with its type" do Puppet::Resource.new("file", "/f").should be_tagged("file") end it "should tag itself with its title if the title is a valid tag" do Puppet::Resource.new("user", "bar").should be_tagged("bar") end it "should not tag itself with its title if the title is a not valid tag" do Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") end it "should allow setting of attributes" do Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" Puppet::Resource.new("file", "/bar", :exported => true).should be_exported end it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do ref = Puppet::Resource.new(:component, "foo") ref.type.should == "Class" ref.title.should == "Foo" end it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do ref = Puppet::Resource.new(:component, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should set the type to 'Class' if it is nil and the title contains no square brackets" do ref = Puppet::Resource.new(nil, "yay") ref.type.should == "Class" ref.title.should == "Yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") ref.type.should == "Foo::Bar" ref.title.should =="baz[yay]" end it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do ref = Puppet::Resource.new("foo::bar[baz]") ref.type.should == "Foo::Bar" ref.title.should =="baz" end it "should be able to extract its information from a Puppet::Type instance" do ral = Puppet::Type.type(:file).new :path => basepath+"/foo" ref = Puppet::Resource.new(ral) ref.type.should == "File" ref.title.should == basepath+"/foo" end it "should fail if the title is nil and the type is not a valid resource reference string" do expect { Puppet::Resource.new("resource-spec-foo") }.to raise_error(ArgumentError) end it 'should fail if strict is set and type does not exist' do expect { Puppet::Resource.new('resource-spec-foo', 'title', {:strict=>true}) }.to raise_error(ArgumentError, 'Invalid resource type resource-spec-foo') end it 'should fail if strict is set and class does not exist' do expect { Puppet::Resource.new('Class', 'resource-spec-foo', {:strict=>true}) }.to raise_error(ArgumentError, 'Could not find declared class resource-spec-foo') end it "should fail if the title is a hash and the type is not a valid resource reference string" do expect { Puppet::Resource.new({:type => "resource-spec-foo", :title => "bar"}) }. to raise_error ArgumentError, /Puppet::Resource.new does not take a hash/ end it "should be taggable" do Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) end it "should have an 'exported' attribute" do resource = Puppet::Resource.new("file", "/f") resource.exported = true resource.exported.should == true resource.should be_exported end describe "and munging its type and title" do describe "when modeling a builtin resource" do it "should be able to find the resource type" do Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) end it "should set its type to the capitalized type name" do Puppet::Resource.new("file", "/my/file").type.should == "File" end end describe "when modeling a defined resource" do describe "that exists" do before do @type = Puppet::Resource::Type.new(:definition, "foo::bar") environment.known_resource_types.add @type end it "should set its type to the capitalized type name" do Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).type.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).resource_type.should equal(@type) end it "should set its title to the provided title" do Puppet::Resource.new("foo::bar", "/my/file", :environment => environment).title.should == "/my/file" end end describe "that does not exist" do it "should set its resource type to the capitalized resource type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end end end describe "when modeling a node" do # Life's easier with nodes, because they can't be qualified. it "should set its type to 'Node' and its title to the provided title" do node = Puppet::Resource.new("node", "foo") node.type.should == "Node" node.title.should == "foo" end end describe "when modeling a class" do it "should set its type to 'Class'" do Puppet::Resource.new("class", "foo").type.should == "Class" end describe "that exists" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") environment.known_resource_types.add @type end it "should set its title to the capitalized, fully qualified resource type" do Puppet::Resource.new("class", "foo::bar", :environment => environment).title.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("class", "foo::bar", :environment => environment).resource_type.should equal(@type) end end describe "that does not exist" do it "should set its type to 'Class' and its title to the capitalized provided name" do klass = Puppet::Resource.new("class", "foo::bar") klass.type.should == "Class" klass.title.should == "Foo::Bar" end end describe "and its name is set to the empty string" do it "should set its title to :main" do Puppet::Resource.new("class", "").title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") environment.known_resource_types.add @type Puppet::Resource.new("class", "", :environment => environment).title.should == :main end end end describe "and its name is set to :main" do it "should set its title to :main" do Puppet::Resource.new("class", :main).title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") environment.known_resource_types.add @type Puppet::Resource.new("class", :main, :environment => environment).title.should == :main end end end end end it "should return nil when looking up resource types that don't exist" do Puppet::Resource.new("foobar", "bar").resource_type.should be_nil end it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") environment.known_resource_types.add type resource = Puppet::Resource.new("foobar", "/my/file", :environment => environment) resource[:yay] = true end it "should be considered equivalent to another resource if their type and title match and no parameters are set" do Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") end it "should be considered equivalent to another resource if their type, title, and parameters are equal" do Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to another resource if their type and title match but parameters are different" do Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to a non-resource" do Puppet::Resource.new("file", "/f").should_not == "foo" end it "should not be considered equivalent to another resource if their types do not match" do Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") end it "should not be considered equivalent to another resource if their titles do not match" do Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") end describe "when setting default parameters" do let(:foo_node) { Puppet::Node.new('foo', :environment => environment) } let(:compiler) { Puppet::Parser::Compiler.new(foo_node) } let(:scope) { Puppet::Parser::Scope.new(compiler) } def ast_string(value) Puppet::Parser::AST::String.new({:value => value}) end it "should fail when asked to set default values and it is not a parser resource" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("default")}) ) resource = Puppet::Resource.new("default_param", "name", :environment => environment) lambda { resource.set_default_parameters(scope) }.should raise_error(Puppet::DevError) end it "should evaluate and set any default values when no value is provided" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => scope) resource.set_default_parameters(scope) resource["a"].should == "a_default_value" end it "should skip attributes with no default value" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("no_default_param", "name", :scope => scope) lambda { resource.set_default_parameters(scope) }.should_not raise_error end it "should return the list of default parameters set" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => ast_string("a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => scope) resource.set_default_parameters(scope).should == ["a"] end describe "when the resource type is :hostclass" do let(:environment_name) { "testing env" } let(:fact_values) { { :a => 1 } } let(:port) { Puppet::Parser::AST::String.new(:value => '80') } let(:apache) { Puppet::Resource::Type.new(:hostclass, 'apache', :arguments => { 'port' => port }) } before do environment.known_resource_types.add(apache) scope.stubs(:host).returns('host') scope.stubs(:environment).returns(environment) scope.stubs(:facts).returns(Puppet::Node::Facts.new("facts", fact_values)) end context "when no value is provided" do before(:each) do Puppet[:binder] = true end let(:resource) do Puppet::Parser::Resource.new("class", "apache", :scope => scope) end it "should query the data_binding terminus using a namespaced key" do Puppet::DataBinding.indirection.expects(:find).with( 'apache::port', all_of(has_key(:environment), has_key(:variables))) resource.set_default_parameters(scope) end it "should use the value from the data_binding terminus" do Puppet::DataBinding.indirection.expects(:find).returns('443') resource.set_default_parameters(scope) resource[:port].should == '443' end it "should use the default value if the data_binding terminus returns nil" do Puppet::DataBinding.indirection.expects(:find).returns(nil) resource.set_default_parameters(scope) resource[:port].should == '80' end it "should fail with error message about data binding on a hiera failure" do Puppet::DataBinding.indirection.expects(:find).raises(Puppet::DataBinding::LookupError, 'Forgettabotit') expect { resource.set_default_parameters(scope) }.to raise_error(Puppet::Error, /Error from DataBinding 'hiera' while looking up 'apache::port':.*Forgettabotit/) end end context "when a value is provided" do let(:port_parameter) do Puppet::Parser::Resource::Param.new( { :name => 'port', :value => '8080' } ) end let(:resource) do Puppet::Parser::Resource.new("class", "apache", :scope => scope, :parameters => [port_parameter]) end it "should not query the data_binding terminus" do Puppet::DataBinding.indirection.expects(:find).never resource.set_default_parameters(scope) end it "should not query the injector" do # enable the injector Puppet[:binder] = true compiler.injector.expects(:find).never resource.set_default_parameters(scope) end it "should use the value provided" do Puppet::DataBinding.indirection.expects(:find).never resource.set_default_parameters(scope).should == [] resource[:port].should == '8080' end end end end describe "when validating all required parameters are present" do it "should be able to validate that all required parameters are present" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "required_param", :arguments => {"a" => nil}) ) lambda { Puppet::Resource.new("required_param", "name", :environment => environment).validate_complete }.should raise_error(Puppet::ParseError) end it "should not fail when all required parameters are present" do environment.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_required_param") ) resource = Puppet::Resource.new("no_required_param", "name", :environment => environment) resource["a"] = "meh" lambda { resource.validate_complete }.should_not raise_error end it "should not validate against builtin types" do lambda { Puppet::Resource.new("file", "/bar").validate_complete }.should_not raise_error end end describe "when referring to a resource with name canonicalization" do it "should canonicalize its own name" do res = Puppet::Resource.new("file", "/path/") res.uniqueness_key.should == ["/path"] res.ref.should == "File[/path/]" end end describe "when running in strict mode" do it "should be strict" do Puppet::Resource.new("file", "/path", :strict => true).should be_strict end it "should fail if invalid parameters are used" do expect { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.to raise_error end it "should fail if the resource type cannot be resolved" do expect { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.to raise_error end end describe "when managing parameters" do before do @resource = Puppet::Resource.new("file", "/my/file") end it "should correctly detect when provided parameters are not valid for builtin types" do Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") end it "should correctly detect when provided parameters are valid for builtin types" do Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") end it "should correctly detect when provided parameters are not valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar") environment.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file", :environment => environment).should_not be_valid_parameter("myparam") end it "should correctly detect when provided parameters are valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) environment.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file", :environment => environment).should be_valid_parameter("myparam") end it "should allow setting and retrieving of parameters" do @resource[:foo] = "bar" @resource[:foo].should == "bar" end it "should allow setting of parameters at initialization" do Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" end it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do @resource[:foo] = "bar" @resource["foo"].should == "bar" end it "should canonicalize set parameter names to treat symbols and strings equivalently" do @resource["foo"] = "bar" @resource[:foo].should == "bar" end it "should set the namevar when asked to set the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:name] = "bob" resource[:myvar].should == "bob" end it "should return the namevar when asked to return the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:myvar] = "test" resource[:name].should == "test" end it "should be able to set the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" expect { resource[:name] = "eh" }.to_not raise_error end it "should be able to return the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" resource[:name].should == "eh" end it "should be able to iterate over parameters" do @resource[:foo] = "bar" @resource[:fee] = "bare" params = {} @resource.each do |key, value| params[key] = value end params.should == {:foo => "bar", :fee => "bare"} end it "should include Enumerable" do @resource.class.ancestors.should be_include(Enumerable) end it "should have a method for testing whether a parameter is included" do @resource[:foo] = "bar" @resource.should be_has_key(:foo) @resource.should_not be_has_key(:eh) end it "should have a method for providing the list of parameters" do @resource[:foo] = "bar" @resource[:bar] = "foo" keys = @resource.keys keys.should be_include(:foo) keys.should be_include(:bar) end it "should have a method for providing the number of parameters" do @resource[:foo] = "bar" @resource.length.should == 1 end it "should have a method for deleting parameters" do @resource[:foo] = "bar" @resource.delete(:foo) @resource[:foo].should be_nil end it "should have a method for testing whether the parameter list is empty" do @resource.should be_empty @resource[:foo] = "bar" @resource.should_not be_empty end it "should be able to produce a hash of all existing parameters" do @resource[:foo] = "bar" @resource[:fee] = "yay" hash = @resource.to_hash hash[:foo].should == "bar" hash[:fee].should == "yay" end it "should not provide direct access to the internal parameters hash when producing a hash" do hash = @resource.to_hash hash[:foo] = "bar" @resource[:foo].should be_nil end it "should use the title as the namevar to the hash if no namevar is present" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource.to_hash[:myvar].should == "bob" end it "should set :name to the title if :name is not present for non-builtin types" do krt = Puppet::Resource::TypeCollection.new("myenv") krt.add Puppet::Resource::Type.new(:definition, :foo) resource = Puppet::Resource.new :foo, "bar" resource.stubs(:known_resource_types).returns krt resource.to_hash[:name].should == "bar" end end describe "when serializing a native type" do before do @resource = Puppet::Resource.new("file", "/my/file") @resource["one"] = "test" @resource["two"] = "other" end it "should produce an equivalent yaml object" do text = @resource.render('yaml') newresource = Puppet::Resource.convert_from('yaml', text) newresource.should equal_attributes_of @resource end end describe "when serializing a defined type" do before do type = Puppet::Resource::Type.new(:definition, "foo::bar") environment.known_resource_types.add type @resource = Puppet::Resource.new('foo::bar', 'xyzzy', :environment => environment) @resource['one'] = 'test' @resource['two'] = 'other' @resource.resource_type end it "doesn't include transient instance variables (#4506)" do expect(@resource.to_yaml_properties).to_not include :@rstype end it "produces an equivalent yaml object" do text = @resource.render('yaml') newresource = Puppet::Resource.convert_from('yaml', text) newresource.should equal_attributes_of @resource end end describe "when converting to a RAL resource" do it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do resource = Puppet::Resource.new("file", basepath+"/my/file") result = resource.to_ral result.must be_instance_of(Puppet::Type.type(:file)) result[:path].should == basepath+"/my/file" end it "should convert to a component instance if the resource type is not of a builtin type" do resource = Puppet::Resource.new("foobar", "somename") result = resource.to_ral result.must be_instance_of(Puppet::Type.type(:component)) result.title.should == "Foobar[somename]" end end describe "when converting to puppet code" do before do @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => { :noop => true, :foo => %w{one two}, :ensure => 'present', } ) end it "should align, sort and add trailing commas to attributes with ensure first" do @resource.to_manifest.should == <<-HEREDOC.gsub(/^\s{8}/, '').gsub(/\n$/, '') one::two { '/my/file': ensure => 'present', foo => ['one', 'two'], noop => 'true', } HEREDOC end end describe "when converting to pson" do def pson_output_should @resource.class.expects(:pson_create).with { |hash| yield hash } end it "should include the pson util module" do Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its type to the provided type" do Puppet::Resource.from_data_hash(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_data_hash(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" end it "should include all tags from the resource" do resource = Puppet::Resource.new("File", "/foo") resource.tag("yay") Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).tags.should == resource.tags end it "should include the file if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.file = "/my/file" Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).file.should == "/my/file" end it "should include the line if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.line = 50 Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).line.should == 50 end it "should include the 'exported' value if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.exported = true Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).exported?.should be_true end it "should set 'exported' to false if no value is set" do resource = Puppet::Resource.new("File", "/foo") Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)).exported?.should be_false end it "should set all of its parameters as the 'parameters' entry" do resource = Puppet::Resource.new("File", "/foo") resource[:foo] = %w{bar eh} resource[:fee] = %w{baz} result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result["foo"].should == %w{bar eh} result["fee"].should == %w{baz} end it "should serialize relationships as reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = Puppet::Resource.new("File", "/bar") result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result[:requires].should == "File[/bar]" end it "should serialize multiple relationships as arrays of reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = [Puppet::Resource.new("File", "/bar"), Puppet::Resource.new("File", "/baz")] result = Puppet::Resource.from_data_hash(PSON.parse(resource.to_pson)) result[:requires].should == [ "File[/bar]", "File[/baz]" ] end end describe "when converting from pson" do def pson_result_should Puppet::Resource.expects(:new).with { |hash| yield hash } end before do @data = { 'type' => "file", 'title' => basepath+"/yay", } end it "should set its type to the provided type" do Puppet::Resource.from_data_hash(@data).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_data_hash(@data).title.should == basepath+"/yay" end it "should tag the resource with any provided tags" do @data['tags'] = %w{foo bar} resource = Puppet::Resource.from_data_hash(@data) resource.tags.should be_include("foo") resource.tags.should be_include("bar") end it "should set its file to the provided file" do @data['file'] = "/foo/bar" Puppet::Resource.from_data_hash(@data).file.should == "/foo/bar" end it "should set its line to the provided line" do @data['line'] = 50 Puppet::Resource.from_data_hash(@data).line.should == 50 end it "should 'exported' to true if set in the pson data" do @data['exported'] = true Puppet::Resource.from_data_hash(@data).exported.should be_true end it "should 'exported' to false if not set in the pson data" do Puppet::Resource.from_data_hash(@data).exported.should be_false end it "should fail if no title is provided" do @data.delete('title') expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError) end it "should fail if no type is provided" do @data.delete('type') expect { Puppet::Resource.from_data_hash(@data) }.to raise_error(ArgumentError) end it "should set each of the provided parameters" do @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} resource = Puppet::Resource.from_data_hash(@data) resource['foo'].should == %w{one two} resource['fee'].should == %w{three four} end it "should convert single-value array parameters to normal values" do @data['parameters'] = {'foo' => %w{one}} resource = Puppet::Resource.from_data_hash(@data) resource['foo'].should == %w{one} end end it "implements copy_as_resource" do resource = Puppet::Resource.new("file", "/my/file") resource.copy_as_resource.should == resource end describe "because it is an indirector model" do it "should include Puppet::Indirector" do Puppet::Resource.should be_is_a(Puppet::Indirector) end it "should have a default terminus" do Puppet::Resource.indirection.terminus_class.should be end it "should have a name" do Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" end end describe "when resolving resources with a catalog" do it "should resolve all resources using the catalog" do catalog = mock 'catalog' resource = Puppet::Resource.new("foo::bar", "yay") resource.catalog = catalog catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) resource.resolve.should == :myresource end end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) res.uniqueness_key.should == [ nil, 'root', '/my/file'] end end describe '#parse_title' do describe 'with a composite namevar' do before do Puppet::Type.newtype(:composite) do newparam(:name) newparam(:value) # Configure two title patterns to match a title that is either # separated with a colon or exclamation point. The first capture # will be used for the :name param, and the second capture will be # used for the :value param. def self.title_patterns identity = lambda {|x| x } reverse = lambda {|x| x.reverse } [ [ /^(.*?):(.*?)$/, [ [:name, identity], [:value, identity], ] ], [ /^(.*?)!(.*?)$/, [ [:name, reverse], [:value, reverse], ] ], ] end end end describe "with no matching title patterns" do subject { Puppet::Resource.new(:composite, 'unmatching title')} it "should raise an exception if no title patterns match" do expect do subject.to_hash end.to raise_error(Puppet::Error, /No set of title patterns matched/) end end describe "with a matching title pattern" do subject { Puppet::Resource.new(:composite, 'matching:title') } it "should not raise an exception if there was a match" do expect do subject.to_hash end.to_not raise_error end it "should set the resource parameters from the parsed title values" do h = subject.to_hash h[:name].should == 'matching' h[:value].should == 'title' end end describe "and multiple title patterns" do subject { Puppet::Resource.new(:composite, 'matching!title') } it "should use the first title pattern that matches" do h = subject.to_hash h[:name].should == 'gnihctam' h[:value].should == 'eltit' end end end end describe "#prune_parameters" do before do Puppet.newtype('blond') do newproperty(:ensure) newproperty(:height) newproperty(:weight) newproperty(:sign) newproperty(:friends) newparam(:admits_to_dying_hair) newparam(:admits_to_age) newparam(:name) end end it "should strip all parameters and strip properties that are nil, empty or absent except for ensure" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'absent', :height => '', :weight => 'absent', :friends => [], :admits_to_age => true, :admits_to_dying_hair => false }) pruned_resource = resource.prune_parameters pruned_resource.should == Puppet::Resource.new("blond", "Bambi", :parameters => {:ensure => 'absent'}) end it "should leave parameters alone if in parameters_to_include" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :admits_to_age => true, :admits_to_dying_hair => false }) pruned_resource = resource.prune_parameters(:parameters_to_include => [:admits_to_dying_hair]) pruned_resource.should == Puppet::Resource.new("blond", "Bambi", :parameters => {:admits_to_dying_hair => false}) end it "should leave properties if not nil, absent or empty" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'silly', :height => '7 ft 5 in', :friends => ['Oprah'], }) pruned_resource = resource.prune_parameters pruned_resource.should == resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'silly', :height => '7 ft 5 in', :friends => ['Oprah'], }) end end end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 1800dd2dc..3028c9e82 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -1,256 +1,256 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/util/autoload' describe Puppet::Util::Autoload do include PuppetSpec::Files before do @autoload = Puppet::Util::Autoload.new("foo", "tmp") @autoload.stubs(:eachdir).yields make_absolute("/my/dir") @loaded = {} @autoload.class.stubs(:loaded).returns(@loaded) end describe "when building the search path" do before :each do ## modulepath/libdir can't be used until after app settings are initialized, so we need to simulate that: Puppet.settings.expects(:app_defaults_initialized?).returns(true).at_least_once @dira = File.expand_path('/a') @dirb = File.expand_path('/b') @dirc = File.expand_path('/c') end it "should collect all of the lib directories that exist in the current environment's module path" do - environment = Puppet::Node::Environment.create(:foo, [@dira, @dirb, @dirc], '') + environment = Puppet::Node::Environment.create(:foo, [@dira, @dirb, @dirc]) Dir.expects(:entries).with(@dira).returns %w{. .. one two} Dir.expects(:entries).with(@dirb).returns %w{. .. one two} Puppet::FileSystem.expects(:directory?).with(@dira).returns true Puppet::FileSystem.expects(:directory?).with(@dirb).returns true Puppet::FileSystem.expects(:directory?).with(@dirc).returns false FileTest.expects(:directory?).with(regexp_matches(%r{two/lib})).times(2).returns true FileTest.expects(:directory?).with(regexp_matches(%r{one/lib})).times(2).returns false @autoload.class.module_directories(environment).should == ["#{@dira}/two/lib", "#{@dirb}/two/lib"] end it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do Puppet[:libdir] = %w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR) @autoload.class.expects(:gem_directories).returns %w{/one /two} @autoload.class.expects(:module_directories).returns %w{/three /four} @autoload.class.search_directories(nil).should == %w{/one /two /three /four} + Puppet[:libdir].split(File::PATH_SEPARATOR) + $LOAD_PATH end end describe "when loading a file" do before do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] FileTest.stubs(:directory?).returns true @time_a = Time.utc(2010, 'jan', 1, 6, 30) File.stubs(:mtime).returns @time_a end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do Puppet::FileSystem.stubs(:exist?).returns true Kernel.expects(:load).raises error lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) end end it "should not raise an error if the file is missing" do @autoload.load("foo").should == false end it "should register loaded files with the autoloader" do Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.class.loaded?("tmp/myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should be seen by loaded? on the instance using the short name" do Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.loaded?("myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") $LOADED_FEATURES.should be_include("tmp/myfile.rb") $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should load the first file in the searchpath" do @autoload.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")] FileTest.stubs(:directory?).returns true Puppet::FileSystem.stubs(:exist?).returns true Kernel.expects(:load).with(make_absolute("/a/tmp/myfile.rb"), optionally(anything)) @autoload.load("myfile") $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should treat equivalent paths to a loaded file as loaded" do Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.class.loaded?("tmp/myfile").should be @autoload.class.loaded?("tmp/./myfile.rb").should be @autoload.class.loaded?("./tmp/myfile.rb").should be @autoload.class.loaded?("tmp/../tmp/myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end end describe "when loading all files" do before do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] FileTest.stubs(:directory?).returns true Dir.stubs(:glob).returns [make_absolute("/a/foo/file.rb")] Puppet::FileSystem.stubs(:exist?).returns true @time_a = Time.utc(2010, 'jan', 1, 6, 30) File.stubs(:mtime).returns @time_a @autoload.class.stubs(:loaded?).returns(false) end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die an if a #{error.to_s} exception is thrown" do Kernel.expects(:load).raises error lambda { @autoload.loadall }.should raise_error(Puppet::Error) end end it "should require the full path to the file" do Kernel.expects(:load).with(make_absolute("/a/foo/file.rb"), optionally(anything)) @autoload.loadall end end describe "when reloading files" do before :each do @file_a = make_absolute("/a/file.rb") @file_b = make_absolute("/b/file.rb") @first_time = Time.utc(2010, 'jan', 1, 6, 30) @second_time = @first_time + 60 end after :each do $LOADED_FEATURES.delete("a/file.rb") $LOADED_FEATURES.delete("b/file.rb") end it "#changed? should return true for a file that was not loaded" do @autoload.class.changed?(@file_a).should be end it "changes should be seen by changed? on the instance using the short name" do File.stubs(:mtime).returns(@first_time) Puppet::FileSystem.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.loaded?("myfile").should be @autoload.changed?("myfile").should_not be File.stubs(:mtime).returns(@second_time) @autoload.changed?("myfile").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end describe "in one directory" do before :each do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] File.expects(:mtime).with(@file_a).returns(@first_time) @autoload.class.mark_loaded("file", @file_a) end it "should reload if mtime changes" do File.stubs(:mtime).with(@file_a).returns(@first_time + 60) Puppet::FileSystem.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed end it "should do nothing if the file is deleted" do File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) Puppet::FileSystem.stubs(:exist?).with(@file_a).returns false Kernel.expects(:load).never @autoload.class.reload_changed end end describe "in two directories" do before :each do @autoload.class.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")] end it "should load b/file when a/file is deleted" do File.expects(:mtime).with(@file_a).returns(@first_time) @autoload.class.mark_loaded("file", @file_a) File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) Puppet::FileSystem.stubs(:exist?).with(@file_a).returns false Puppet::FileSystem.stubs(:exist?).with(@file_b).returns true File.stubs(:mtime).with(@file_b).returns @first_time Kernel.expects(:load).with(@file_b, optionally(anything)) @autoload.class.reload_changed @autoload.class.send(:loaded)["file"].should == [@file_b, @first_time] end it "should load a/file when b/file is loaded and a/file is created" do File.stubs(:mtime).with(@file_b).returns @first_time Puppet::FileSystem.stubs(:exist?).with(@file_b).returns true @autoload.class.mark_loaded("file", @file_b) File.stubs(:mtime).with(@file_a).returns @first_time Puppet::FileSystem.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed @autoload.class.send(:loaded)["file"].should == [@file_a, @first_time] end end end describe "#cleanpath" do it "should leave relative paths relative" do path = "hello/there" Puppet::Util::Autoload.cleanpath(path).should == path end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should convert c:\ to c:/" do Puppet::Util::Autoload.cleanpath('c:\\').should == 'c:/' end end end describe "#expand" do it "should expand relative to the autoloader's prefix" do @autoload.expand('bar').should == 'tmp/bar' end end end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index fa536703a..53231ef48 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -1,599 +1,599 @@ #! /usr/bin/env ruby require 'spec_helper' describe "RDoc::Parser", :if => Puppet.features.rdoc1? do before :all do require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' end include PuppetSpec::Files before :each do stub_file = stub('init.pp', :stat => stub()) # Ruby 1.8.7 needs the following call to be stubs and not expects Puppet::FileSystem.stubs(:stat).with('init.pp').returns stub() # stub_file @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.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once parser.expects(:file=).with("module/manifests/init.pp") parser.expects(:file=).with do |args| args =~ /.*\/etc\/manifests\/site.pp/ end @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(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @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(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan.should be_a(RDoc::PuppetTopLevel) end it "should scan the top level even if the file has already parsed" do known_type = stub 'known_types' env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(env) env.stubs(:known_resource_types).returns(known_type) known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true) @parser.expects(:scan_top_level) @parser.scan end end describe "when scanning top level entities" do - let(:environment) { Puppet::Node::Environment.create(:env, [], '') } + let(:environment) { Puppet::Node::Environment.create(:env, []) } before :each do @resource_type_collection = resource_type_collection = stub_everything('resource_type_collection') environment.stubs(:known_resource_types).returns(@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?).with("module/README").returns(true) FileTest.stubs(:readable?).with("module/README.rdoc").returns(false) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:add_comment).with("readme", "module/manifests/init.pp") @parser.scan_top_level(@topcontainer, environment) end it "should read any present README.rdoc as module documentation" do FileTest.stubs(:readable?).with("module/README.rdoc").returns(true) FileTest.stubs(:readable?).with("module/README").returns(false) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:add_comment).with("readme", "module/manifests/init.pp") @parser.scan_top_level(@topcontainer, environment) end it "should prefer README.rdoc over README as module documentation" do FileTest.stubs(:readable?).with("module/README.rdoc").returns(true) FileTest.stubs(:readable?).with("module/README").returns(true) File.stubs(:open).with("module/README", "r").returns("readme") File.stubs(:open).with("module/README.rdoc", "r").returns("readme.rdoc") @parser.stubs(:parse_elements) @module.expects(:add_comment).with("readme.rdoc", "module/manifests/init.pp") @parser.scan_top_level(@topcontainer, environment) 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, environment) 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, environment) end it "should set the module as global if we parse the global manifests (ie __site__ module)" do @parser.stubs(:split_module).returns(RDoc::Parser::SITE) @parser.stubs(:parse_elements) @topcontainer.expects(:global=).with(true) @parser.scan_top_level(@topcontainer, environment) 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, environment) end it "should defer ast parsing to parse_elements for this module" do @parser.expects(:parse_elements).with(@module, @resource_type_collection) @parser.scan_top_level(@topcontainer, environment) end it "should defer plugins parsing to parse_plugins for this module" do @parser.input_file_name = "module/lib/puppet/parser/function.rb" @parser.expects(:parse_plugins).with(@module) @parser.scan_top_level(@topcontainer, environment) end end describe "when finding modules from filepath" do let(:environment) { Puppet::FileSystem.expects(:directory?).with("/path/to/modules").at_least_once.returns(true) - Puppet::Node::Environment.create(:env, ["/path/to/modules"], '') + Puppet::Node::Environment.create(:env, ["/path/to/modules"]) } it "should return the module name for modulized puppet manifests" do File.stubs(:identical?).with("/path/to/modules", "/path/to/modules").returns(true) @parser.split_module("/path/to/modules/mymodule/manifests/init.pp", environment).should == "mymodule" end it "should return for manifests not under module path" do File.stubs(:identical?).returns(false) @parser.split_module("/path/to/manifests/init.pp", environment).should == RDoc::Parser::SITE end it "should handle windows paths with drive letters", :if => Puppet.features.microsoft_windows? && Puppet.features.rdoc1? do @parser.split_module("C:/temp/init.pp", environment).should == RDoc::Parser::SITE 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 = resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser.instance_eval { @known_resource_types = 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, @resource_type_collection) 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, @resource_type_collection) 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, @resource_type_collection) 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, @resource_type_collection) 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, @resource_type_collection) 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, @resource_type_collection) 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, @resource_type_collection) 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(:add_comment).with("mydoc", "file") @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[:document_all] = 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(:add_comment).with("mydoc", "file") @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[:document_all] = 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", :to_s => "myclass" Puppet::Parser::AST::Function.new( :name => name, :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).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\"]" Puppet::Parser::AST::Function.new( :name => 'realize', :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).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::BlockExpression).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::BlockExpression).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' @stmt = Puppet::Parser::AST::Resource.new( :type => "File", :instances => Puppet::Parser::AST::BlockExpression.new(:children => [ Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::Name.new(:value => "myfile"), :parameters => Puppet::Parser::AST::BlockExpression.new(:children => []) ) ]), :doc => 'mydoc' ) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).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/lib/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/lib/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