diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb index 8b79212a2..8cd2b5796 100755 --- a/spec/integration/defaults_spec.rb +++ b/spec/integration/defaults_spec.rb @@ -1,234 +1,234 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/defaults' describe "Puppet defaults" do describe "when default_manifest is set" do it "returns ./manifests by default" do expect(Puppet[:default_manifest]).to eq('./manifests') end end describe "when disable_per_environment_manifest is set" do it "returns false by default" do expect(Puppet[:disable_per_environment_manifest]).to eq(false) end it "errors when set to true and default_manifest is not an absolute path" do expect { Puppet[:default_manifest] = './some/relative/manifest.pp' Puppet[:disable_per_environment_manifest] = true }.to raise_error Puppet::Settings::ValidationError, /'default_manifest' setting must be.*absolute/ end end describe "when setting the :factpath" do it "should add the :factpath to Facter's search paths" do Facter.expects(:search).with("/my/fact/path") Puppet.settings[:factpath] = "/my/fact/path" end end describe "when setting the :certname" do it "should fail if the certname is not downcased" do expect { Puppet.settings[:certname] = "Host.Domain.Com" }.to raise_error(ArgumentError) end end describe "when setting :node_name_value" do it "should default to the value of :certname" do Puppet.settings[:certname] = 'blargle' Puppet.settings[:node_name_value].should == 'blargle' end end describe "when setting the :node_name_fact" do it "should fail when also setting :node_name_value" do lambda do Puppet.settings[:node_name_value] = "some value" Puppet.settings[:node_name_fact] = "some_fact" end.should raise_error("Cannot specify both the node_name_value and node_name_fact settings") end it "should not fail when using the default for :node_name_value" do lambda do Puppet.settings[:node_name_fact] = "some_fact" end.should_not raise_error end end it "should have a clientyamldir setting" do Puppet.settings[:clientyamldir].should_not be_nil end it "should have different values for the yamldir and clientyamldir" do Puppet.settings[:yamldir].should_not == Puppet.settings[:clientyamldir] end it "should have a client_datadir setting" do Puppet.settings[:client_datadir].should_not be_nil end it "should have different values for the server_datadir and client_datadir" do Puppet.settings[:server_datadir].should_not == Puppet.settings[:client_datadir] end # See #1232 it "should not specify a user or group for the clientyamldir" do Puppet.settings.setting(:clientyamldir).owner.should be_nil Puppet.settings.setting(:clientyamldir).group.should be_nil end it "should use the service user and group for the yamldir" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.stubs(:service_group_available?).returns true Puppet.settings.setting(:yamldir).owner.should == Puppet.settings[:user] Puppet.settings.setting(:yamldir).group.should == Puppet.settings[:group] end it "should specify that the host private key should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostprivkey).owner.should == Puppet.settings[:user] end it "should specify that the host certificate should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostcert).owner.should == Puppet.settings[:user] end [:modulepath, :factpath].each do |setting| it "should configure '#{setting}' not to be a file setting, so multi-directory settings are acceptable" do Puppet.settings.setting(setting).should be_instance_of(Puppet::Settings::PathSetting) end end - describe "on a Unix-like platform it", :as_platform => :posix do + describe "on a Unix-like platform it", :if => Puppet.features.posix? do it "should add /usr/sbin and /sbin to the path if they're not there" do Puppet::Util.withenv("PATH" => "/usr/bin#{File::PATH_SEPARATOR}/usr/local/bin") do Puppet.settings[:path] = "none" # this causes it to ignore the setting ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/usr/sbin") ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/sbin") end end end - describe "on a Windows-like platform it", :as_platform => :windows do + describe "on a Windows-like platform it", :if => Puppet.features.microsoft_windows? do it "should not add anything" do path = "c:\\windows\\system32#{File::PATH_SEPARATOR}c:\\windows" Puppet::Util.withenv("PATH" => path) do Puppet.settings[:path] = "none" # this causes it to ignore the setting ENV["PATH"].should == path end end end it "should default to pson for the preferred serialization format" do Puppet.settings.value(:preferred_serialization_format).should == "pson" end it "should have a setting for determining the configuration version and should default to an empty string" do Puppet.settings[:config_version].should == "" end describe "when enabling reports" do it "should use the default server value when report server is unspecified" do Puppet.settings[:server] = "server" Puppet.settings[:report_server].should == "server" end it "should use the default masterport value when report port is unspecified" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port].should == "1234" end it "should use report_port when set" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port] = "5678" Puppet.settings[:report_port].should == "5678" end end it "should have a :caname setting that defaults to the cert name" do Puppet.settings[:certname] = "foo" Puppet.settings[:ca_name].should == "Puppet CA: foo" end it "should have a 'prerun_command' that defaults to the empty string" do Puppet.settings[:prerun_command].should == "" end it "should have a 'postrun_command' that defaults to the empty string" do Puppet.settings[:postrun_command].should == "" end it "should have a 'certificate_revocation' setting that defaults to true" do Puppet.settings[:certificate_revocation].should be_true end describe "reportdir" do subject { Puppet.settings[:reportdir] } it { should == "#{Puppet[:vardir]}/reports" } end describe "reporturl" do subject { Puppet.settings[:reporturl] } it { should == "http://localhost:3000/reports/upload" } end describe "when configuring color" do subject { Puppet.settings[:color] } it { should == "ansi" } end describe "daemonize" do it "should default to true", :unless => Puppet.features.microsoft_windows? do Puppet.settings[:daemonize].should == true end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should default to false" do Puppet.settings[:daemonize].should == false end it "should raise an error if set to true" do expect { Puppet.settings[:daemonize] = true }.to raise_error(/Cannot daemonize on Windows/) end end end describe "diff" do it "should default to 'diff' on POSIX", :unless => Puppet.features.microsoft_windows? do Puppet.settings[:diff].should == 'diff' end it "should default to '' on Windows", :if => Puppet.features.microsoft_windows? do Puppet.settings[:diff].should == '' end end describe "when configuring hiera" do it "should have a hiera_config setting" do Puppet.settings[:hiera_config].should_not be_nil end end describe "when configuring the data_binding terminus" do it "should have a data_binding_terminus setting" do Puppet.settings[:data_binding_terminus].should_not be_nil end it "should be set to hiera by default" do Puppet.settings[:data_binding_terminus].should == :hiera end end describe "agent_catalog_run_lockfile" do it "(#2888) is not a file setting so it is absent from the Settings catalog" do Puppet.settings.setting(:agent_catalog_run_lockfile).should_not be_a_kind_of Puppet::Settings::FileSetting Puppet.settings.setting(:agent_catalog_run_lockfile).should be_a Puppet::Settings::StringSetting end end end diff --git a/spec/shared_behaviours/path_parameters.rb b/spec/shared_behaviours/path_parameters.rb index d5f06875b..a33a24365 100755 --- a/spec/shared_behaviours/path_parameters.rb +++ b/spec/shared_behaviours/path_parameters.rb @@ -1,160 +1,160 @@ # In order to use this correctly you must define a method to get an instance # of the type being tested, so that this code can remain generic: # # it_should_behave_like "all path parameters", :path do # def instance(path) # Puppet::Type.type(:example).new( # :name => 'foo', :require => 'bar', :path_param => path # ) # end # # That method will be invoked for each test to create the instance that we # subsequently test through the system; you should ensure that the minimum of # possible attributes are set to keep the tests clean. # # You must also pass the symbolic name of the parameter being tested to the # block, and optionally can pass a hash of additional options to the block. # # The known options are: # :array :: boolean, does this support arrays of paths, default true. shared_examples_for "all pathname parameters with arrays" do |win32| path_types = { "unix absolute" => %q{/foo/bar}, "unix relative" => %q{foo/bar}, "win32 non-drive absolute" => %q{\foo\bar}, "win32 non-drive relative" => %q{foo\bar}, "win32 drive absolute" => %q{c:\foo\bar}, "win32 drive relative" => %q{c:foo\bar} } describe "when given an array of paths" do (1..path_types.length).each do |n| path_types.keys.combination(n) do |set| data = path_types.collect { |k, v| set.member?(k) ? v : nil } .compact has_relative = set.find { |k| k =~ /relative/ or k =~ /non-drive/ } has_windows = set.find { |k| k =~ /win32/ } has_unix = set.find { |k| k =~ /unix/ } if has_relative or (has_windows and !win32) or (has_unix and win32) reject = true else reject = false end it "should #{reject ? 'reject' : 'accept'} #{set.join(", ")}" do if reject then expect { instance(data) }. to raise_error Puppet::Error, /fully qualified/ else instance = instance(data) instance[@param].should == data end end it "should #{reject ? 'reject' : 'accept'} #{set.join(", ")} doubled" do if reject then expect { instance(data + data) }. to raise_error Puppet::Error, /fully qualified/ else instance = instance(data + data) instance[@param].should == (data + data) end end end end end end shared_examples_for "all path parameters" do |param, options| # Extract and process options to the block. options ||= {} array = options[:array].nil? ? true : options.delete(:array) if options.keys.length > 0 then fail "unknown options for 'all path parameters': " + options.keys.sort.join(', ') end def instance(path) fail "we didn't implement the 'instance(path)' method in the it_should_behave_like block" end ######################################################################## # The actual testing code... before :all do @param = param end - describe "on a Unix-like platform it", :as_platform => :posix do + describe "on a Unix-like platform it", :if => Puppet.features.posix? do if array then it_should_behave_like "all pathname parameters with arrays", false end it "should accept a fully qualified path" do path = File.join('', 'foo') instance = instance(path) instance[@param].should == path end it "should give a useful error when the path is not absolute" do path = 'foo' expect { instance(path) }. to raise_error Puppet::Error, /fully qualified/ end { "Unix" => '/', "Win32" => '\\' }.each do |style, slash| %w{q Q a A z Z c C}.sort.each do |drive| it "should reject drive letter '#{drive}' with #{style} path separators" do path = "#{drive}:#{slash}Program Files" expect { instance(path) }. to raise_error Puppet::Error, /fully qualified/ end end end end - describe "on a Windows-like platform it", :as_platform => :windows do + describe "on a Windows-like platform it", :if => Puppet.features.microsoft_windows? do if array then it_should_behave_like "all pathname parameters with arrays", true end it "should reject a fully qualified unix path" do path = '/foo' expect { instance(path) }.to raise_error(Puppet::Error, /fully qualified/) end it "should give a useful error when the path is not absolute" do path = 'foo' expect { instance(path) }. to raise_error Puppet::Error, /fully qualified/ end it "also accepts Unix style path separators" do path = 'C:/Program Files' instance = instance(path) instance[@param].should == path end { "Unix" => '/', "Win32" => '\\' }.each do |style, slash| %w{q Q a A z Z c C}.sort.each do |drive| it "should accept drive letter '#{drive}' with #{style} path separators " do path = "#{drive}:#{slash}Program Files" instance = instance(path) instance[@param].should == path end end end { "UNC paths" => %q{\\\\foo\bar}, "unparsed local paths" => %q{\\\\?\c:\foo}, "unparsed UNC paths" => %q{\\\\?\foo\bar} }.each do |name, path| it "should accept #{name} as absolute" do instance = instance(path) instance[@param].should == path end end end end diff --git a/spec/shared_contexts/platform.rb b/spec/shared_contexts/platform.rb deleted file mode 100644 index 2e6d4d7a6..000000000 --- a/spec/shared_contexts/platform.rb +++ /dev/null @@ -1,52 +0,0 @@ -# Contexts for stubbing platforms -# In a describe or context block, adding :as_platform => :windows or -# :as_platform => :posix will stub the relevant Puppet features, as well as -# the behavior of Ruby's filesystem methods by changing File::ALT_SEPARATOR. - -shared_context "windows", :as_platform => :windows do - before :each do - Facter.stubs(:value).with(:operatingsystem).returns 'Windows' - Facter.stubs(:value).with(:osfamily).returns 'windows' - Puppet.features.stubs(:microsoft_windows?).returns(true) - Puppet.features.stubs(:posix?).returns(false) - end - - around do |example| - file_alt_separator = File::ALT_SEPARATOR - file_path_separator = File::PATH_SEPARATOR - - # prevent Ruby from warning about changing a constant - with_verbose_disabled do - File::ALT_SEPARATOR = '\\' - File::PATH_SEPARATOR = ';' - end - example.run - with_verbose_disabled do - File::ALT_SEPARATOR = file_alt_separator - File::PATH_SEPARATOR = file_path_separator - end - end -end - -shared_context "posix", :as_platform => :posix do - before :each do - Puppet.features.stubs(:microsoft_windows?).returns(false) - Puppet.features.stubs(:posix?).returns(true) - end - - around do |example| - file_alt_separator = File::ALT_SEPARATOR - file_path_separator = File::PATH_SEPARATOR - - # prevent Ruby from warning about changing a constant - with_verbose_disabled do - File::ALT_SEPARATOR = nil - File::PATH_SEPARATOR = ':' - end - example.run - with_verbose_disabled do - File::ALT_SEPARATOR = file_alt_separator - File::PATH_SEPARATOR = file_path_separator - end - end -end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index 68197dee8..95319a624 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -1,327 +1,327 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/agent' class AgentTestClient def run # no-op end def stop # no-op end end def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Agent do before do Puppet::Status.indirection.stubs(:find).returns Puppet::Status.new("version" => Puppet.version) @agent = Puppet::Agent.new(AgentTestClient, false) # So we don't actually try to hit the filesystem. @agent.stubs(:lock).yields # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. without_warnings { Puppet::Application = Class.new(Puppet::Application) } Puppet::Application.stubs(:clear?).returns(true) Puppet::Application.class_eval do class << self def controlled_run(&block) block.call end end end end after do # restore Puppet::Application from stub-safe subclass, and silence warnings without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should set its client class at initialization" do Puppet::Agent.new("foo", false).client_class.should == "foo" end it "should include the Locker module" do Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker) end it "should create an instance of its client class and run it when asked to run" do client = mock 'client' AgentTestClient.expects(:new).returns client client.expects(:run) @agent.stubs(:running?).returns false @agent.stubs(:disabled?).returns false @agent.run end it "should be considered running if the lock file is locked" do lockfile = mock 'lockfile' @agent.expects(:lockfile).returns(lockfile) lockfile.expects(:locked?).returns true @agent.should be_running end describe "when being run" do before do AgentTestClient.stubs(:lockfile_path).returns "/my/lock" @agent.stubs(:running?).returns false @agent.stubs(:disabled?).returns false end it "should splay" do @agent.expects(:splay) @agent.run end it "should do nothing if already running" do @agent.expects(:running?).returns true AgentTestClient.expects(:new).never @agent.run end it "should do nothing if disabled" do @agent.expects(:disabled?).returns(true) AgentTestClient.expects(:new).never @agent.run end it "(#11057) should notify the user about why a run is skipped" do Puppet::Application.stubs(:controlled_run).returns(false) Puppet::Application.stubs(:run_status).returns('MOCK_RUN_STATUS') # This is the actual test that we inform the user why the run is skipped. # We assume this information is contained in # Puppet::Application.run_status Puppet.expects(:notice).with(regexp_matches(/MOCK_RUN_STATUS/)) @agent.run end it "should display an informative message if the agent is administratively disabled" do @agent.expects(:disabled?).returns true @agent.expects(:disable_message).returns "foo" Puppet.expects(:notice).with(regexp_matches(/Skipping run of .*; administratively disabled.*\(Reason: 'foo'\)/)) @agent.run end it "should use Puppet::Application.controlled_run to manage process state behavior" do calls = sequence('calls') Puppet::Application.expects(:controlled_run).yields.in_sequence(calls) AgentTestClient.expects(:new).once.in_sequence(calls) @agent.run end it "should not fail if a client class instance cannot be created" do AgentTestClient.expects(:new).raises "eh" Puppet.expects(:err) @agent.run end it "should not fail if there is an exception while running its client" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).raises "eh" Puppet.expects(:err) @agent.run end it "should use a filesystem lock to restrict multiple processes running the agent" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client @agent.expects(:lock) client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it @agent.run end it "should make its client instance available while running" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with { @agent.client.should equal(client); true } @agent.run end it "should run the client instance with any arguments passed to it" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with(:pluginsync => true, :other => :options) @agent.run(:other => :options) end it "should return the agent result" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client @agent.expects(:lock).returns(:result) @agent.run.should == :result end - describe "when should_fork is true", :as_platform => :posix do + describe "when should_fork is true", :if => Puppet.features.posix? do before do @agent = Puppet::Agent.new(AgentTestClient, true) # So we don't actually try to hit the filesystem. @agent.stubs(:lock).yields Kernel.stubs(:fork) Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => 0)] @agent.stubs(:exit) end it "should run the agent in a forked process" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run) Kernel.expects(:fork).yields @agent.run end it "should exit child process if child exit" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).raises(SystemExit) Kernel.expects(:fork).yields @agent.expects(:exit).with(-1) @agent.run end it "should re-raise exit happening in the child" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => -1)] lambda { @agent.run }.should raise_error(SystemExit) end it "should re-raise NoMoreMemory happening in the child" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => -2)] lambda { @agent.run }.should raise_error(NoMemoryError) end it "should return the child exit code" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => 777)] @agent.run.should == 777 end it "should return the block exit code as the child exit code" do Kernel.expects(:fork).yields @agent.expects(:exit).with(777) @agent.run_in_fork { 777 } end end - describe "on Windows", :as_platform => :windows do + describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should never fork" do agent = Puppet::Agent.new(AgentTestClient, true) expect(agent.should_fork).to be_false end end end describe "when splaying" do before do Puppet[:splay] = true Puppet[:splaylimit] = "10" end it "should do nothing if splay is disabled" do Puppet[:splay] = false @agent.expects(:sleep).never @agent.splay end it "should do nothing if it has already splayed" do @agent.expects(:splayed?).returns true @agent.expects(:sleep).never @agent.splay end it "should log that it is splaying" do @agent.stubs :sleep Puppet.expects :info @agent.splay end it "should sleep for a random portion of the splaylimit plus 1" do Puppet[:splaylimit] = "50" @agent.expects(:rand).with(51).returns 10 @agent.expects(:sleep).with(10) @agent.splay end it "should mark that it has splayed" do @agent.stubs(:sleep) @agent.splay @agent.should be_splayed end end describe "when checking execution state" do describe 'with regular run status' do before :each do Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(false) Puppet::Application.stubs(:clear?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a stop requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(true) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be true for :stopping?' do @agent.stopping?.should be_true end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a restart requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(true) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be true for :needing_restart?' do @agent.needing_restart?.should be_true end end end end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 71b88bb51..4aa74afaf 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -1,222 +1,222 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do require 'puppet_spec/files' include PuppetSpec::Files let(:path) { Puppet[:plugindest] } let(:source) { 'puppet://puppet/plugins' } it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end it "should require a path and a source at initialization" do lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) end it "should set the name, path and source appropriately" do dler = Puppet::Configurer::Downloader.new("facts", "path", "source") dler.name.should == "facts" dler.path.should == "path" dler.source.should == "source" end def downloader(options = {}) options[:name] ||= "facts" options[:path] ||= path options[:source_permissions] ||= :ignore Puppet::Configurer::Downloader.new(options[:name], options[:path], source, options[:ignore], options[:environment], options[:source_permissions]) end def generate_file_resource(options = {}) dler = downloader(options) dler.file end describe "when creating the file that does the downloading" do it "should create a file instance with the right path and source" do file = generate_file_resource(:path => path, :source => source) expect(file[:path]).to eq(path) expect(file[:source]).to eq([source]) end it "should tag the file with the downloader name" do name = "mydownloader" file = generate_file_resource(:name => name) expect(file[:tag]).to eq([name]) end it "should always recurse" do file = generate_file_resource expect(file[:recurse]).to be_true end it "should always purge" do file = generate_file_resource expect(file[:purge]).to be_true end it "should never be in noop" do file = generate_file_resource expect(file[:noop]).to be_false end it "should set source_permissions to ignore by default" do file = generate_file_resource expect(file[:source_permissions]).to eq(:ignore) end it "should allow source_permissions to be overridden" do file = generate_file_resource(:source_permissions => :use) expect(file[:source_permissions]).to eq(:use) end - describe "on POSIX", :as_platform => :posix do + describe "on POSIX", :if => Puppet.features.posix? do it "should always set the owner to the current UID" do Process.expects(:uid).returns 51 file = generate_file_resource(:path => '/path') expect(file[:owner]).to eq(51) end it "should always set the group to the current GID" do Process.expects(:gid).returns 61 file = generate_file_resource(:path => '/path') expect(file[:group]).to eq(61) end end - describe "on Windows", :as_platform => :windows do + describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should omit the owner" do file = generate_file_resource(:path => 'C:/path') expect(file[:owner]).to be_nil end it "should omit the group" do file = generate_file_resource(:path => 'C:/path') expect(file[:group]).to be_nil end end it "should always force the download" do file = generate_file_resource expect(file[:force]).to be_true end it "should never back up when downloading" do file = generate_file_resource expect(file[:backup]).to be_false end it "should support providing an 'ignore' parameter" do file = generate_file_resource(:ignore => '.svn') expect(file[:ignore]).to eq(['.svn']) end it "should split the 'ignore' parameter on whitespace" do file = generate_file_resource(:ignore => '.svn CVS') expect(file[:ignore]).to eq(['.svn', 'CVS']) end end describe "when creating the catalog to do the downloading" do before do @path = make_absolute("/download/path") @dler = Puppet::Configurer::Downloader.new("foo", @path, make_absolute("source")) end it "should create a catalog and add the file to it" do catalog = @dler.catalog catalog.resources.size.should == 1 catalog.resources.first.class.should == Puppet::Type::File catalog.resources.first.name.should == @path end it "should specify that it is not managing a host catalog" do @dler.catalog.host_config.should == false end end describe "when downloading" do before do @dl_name = tmpfile("downloadpath") source_name = tmpfile("source") File.open(source_name, 'w') {|f| f.write('hola mundo') } env = Puppet::Node::Environment.remote('foo') @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name, Puppet[:pluginsignore], env) end it "should not skip downloaded resources when filtering on tags" do Puppet[:tags] = 'maytag' @dler.evaluate Puppet::FileSystem.exist?(@dl_name).should be_true end it "should log that it is downloading" do Puppet.expects(:info) @dler.evaluate end it "should return all changed file paths" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) @dler.evaluate.should == %w{/changed/file} end it "should yield the resources if a block is given" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) yielded = nil @dler.evaluate { |r| yielded = r } yielded.should == resource end it "should catch and log exceptions" do Puppet.expects(:err) # The downloader creates a new catalog for each apply, and really the only object # that it is possible to stub for the purpose of generating a puppet error Puppet::Resource::Catalog.any_instance.stubs(:apply).raises(Puppet::Error, "testing") lambda { @dler.evaluate }.should_not raise_error end end end diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb index f955d7128..99141fc34 100755 --- a/spec/unit/parser/functions/generate_spec.rb +++ b/spec/unit/parser/functions/generate_spec.rb @@ -1,136 +1,136 @@ #! /usr/bin/env ruby require 'spec_helper' describe "the generate function" do include PuppetSpec::Files before :all do Puppet::Parser::Functions.autoloader.loadall end let :node do Puppet::Node.new('localhost') end let :compiler do Puppet::Parser::Compiler.new(node) end let :scope do Puppet::Parser::Scope.new(compiler) end it "should exist" do Puppet::Parser::Functions.function("generate").should == "function_generate" end it "accept a fully-qualified path as a command" do command = File.expand_path('/command/foo') Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == "yay" end it "should not accept a relative path as a command" do lambda { scope.function_generate(["command"]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing illegal characters" do lambda { scope.function_generate([File.expand_path('/##/command')]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing spaces" do lambda { scope.function_generate([File.expand_path('/com mand')]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing '..'" do command = File.expand_path("/command/../") lambda { scope.function_generate([command]) }.should raise_error(Puppet::ParseError) end it "should execute the generate script with the correct working directory" do command = File.expand_path("/command") Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should accept the tilde in the path" do command = "C:/DOCUME~1/ADMINI~1/foo.bat" Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept lower-case drive letters" do command = 'd:/command/foo' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept upper-case drive letters" do command = 'D:/command/foo' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept forward and backslashes in the path" do command = 'D:\command/foo\bar' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should reject colons when not part of the drive letter" do lambda { scope.function_generate(['C:/com:mand']) }.should raise_error(Puppet::ParseError) end it "should reject root drives" do lambda { scope.function_generate(['C:/']) }.should raise_error(Puppet::ParseError) end end - describe "on non-Windows", :as_platform => :posix do + describe "on POSIX", :if => Puppet.features.posix? do it "should reject backslashes" do lambda { scope.function_generate(['/com\\mand']) }.should raise_error(Puppet::ParseError) end it "should accept plus and dash" do command = "/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-/foo" Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end end let :command do cmd = tmpfile('function_generate') if Puppet.features.microsoft_windows? cmd += '.bat' text = '@echo off' + "\n" + 'echo a-%1 b-%2' else text = '#!/bin/sh' + "\n" + 'echo a-$1 b-$2' end File.open(cmd, 'w') {|fh| fh.puts text } File.chmod 0700, cmd cmd end after :each do File.delete(command) if Puppet::FileSystem.exist?(command) end it "returns the output as a String" do scope.function_generate([command]).class.should == String end it "should call generator with no arguments" do scope.function_generate([command]).should == "a- b-\n" end it "should call generator with one argument" do scope.function_generate([command, 'one']).should == "a-one b-\n" end it "should call generator with wo arguments" do scope.function_generate([command, 'one', 'two']).should == "a-one b-two\n" end it "should fail if generator is not absolute" do expect { scope.function_generate(['boo']) }.to raise_error(Puppet::ParseError) end it "should fail if generator fails" do expect { scope.function_generate(['/boo']) }.to raise_error(Puppet::ParseError) end end diff --git a/spec/unit/provider/exec/windows_spec.rb b/spec/unit/provider/exec/windows_spec.rb index 592610436..eb3f91eaf 100755 --- a/spec/unit/provider/exec/windows_spec.rb +++ b/spec/unit/provider/exec/windows_spec.rb @@ -1,107 +1,107 @@ #! /usr/bin/env ruby require 'spec_helper' -describe Puppet::Type.type(:exec).provider(:windows), :as_platform => :windows do +describe Puppet::Type.type(:exec).provider(:windows), :if => Puppet.features.microsoft_windows? do include PuppetSpec::Files let(:resource) { Puppet::Type.type(:exec).new(:title => 'C:\foo', :provider => :windows) } let(:provider) { described_class.new(resource) } after :all do # This provider may not be suitable on some machines, so we want to reset # the default so it isn't used by mistake in future specs. Puppet::Type.type(:exec).defaultprovider = nil end describe "#extractexe" do describe "when the command has no arguments" do it "should return the command if it's quoted" do provider.extractexe('"foo"').should == 'foo' end it "should return the command if it's quoted and contains spaces" do provider.extractexe('"foo bar"').should == 'foo bar' end it "should return the command if it's not quoted" do provider.extractexe('foo').should == 'foo' end end describe "when the command has arguments" do it "should return the command if it's quoted" do provider.extractexe('"foo" bar baz').should == 'foo' end it "should return the command if it's quoted and contains spaces" do provider.extractexe('"foo bar" baz "quux quiz"').should == 'foo bar' end it "should return the command if it's not quoted" do provider.extractexe('foo bar baz').should == 'foo' end end end describe "#checkexe" do describe "when the command is absolute", :if => Puppet.features.microsoft_windows? do it "should return if the command exists and is a file" do command = tmpfile('command') FileUtils.touch(command) provider.checkexe(command).should == nil end it "should fail if the command doesn't exist" do command = tmpfile('command') expect { provider.checkexe(command) }.to raise_error(ArgumentError, "Could not find command '#{command}'") end it "should fail if the command isn't a file" do command = tmpfile('command') FileUtils.mkdir(command) expect { provider.checkexe(command) }.to raise_error(ArgumentError, "'#{command}' is a directory, not a file") end end describe "when the command is relative" do describe "and a path is specified" do before :each do provider.stubs(:which) end it "should search for executables with no extension" do provider.resource[:path] = [File.expand_path('/bogus/bin')] provider.expects(:which).with('foo').returns('foo') provider.checkexe('foo') end it "should fail if the command isn't in the path" do expect { provider.checkexe('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end end it "should fail if no path is specified" do expect { provider.checkexe('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end end end describe "#validatecmd" do it "should fail if the command isn't absolute and there is no path" do expect { provider.validatecmd('foo') }.to raise_error(Puppet::Error, /'foo' is not qualified and no path was specified/) end it "should not fail if the command is absolute and there is no path" do provider.validatecmd('C:\foo').should == nil end it "should not fail if the command is not absolute and there is a path" do resource[:path] = 'C:\path;C:\another_path' provider.validatecmd('foo').should == nil end end end diff --git a/spec/unit/provider/service/openwrt_spec.rb b/spec/unit/provider/service/openwrt_spec.rb index 8d385d175..4550d6623 100755 --- a/spec/unit/provider/service/openwrt_spec.rb +++ b/spec/unit/provider/service/openwrt_spec.rb @@ -1,109 +1,109 @@ #! /usr/bin/env ruby # # Unit testing for the OpenWrt service Provider # require 'spec_helper' -describe Puppet::Type.type(:service).provider(:openwrt), :as_platform => :posix do +describe Puppet::Type.type(:service).provider(:openwrt), :if => Puppet.features.posix? do let(:resource) do resource = stub 'resource' resource.stubs(:[]).returns(nil) resource.stubs(:[]).with(:name).returns "myservice" resource.stubs(:[]).with(:path).returns ["/etc/init.d"] resource end let(:provider) do provider = described_class.new provider.stubs(:get).with(:hasstatus).returns false provider end before :each do resource.stubs(:provider).returns provider provider.resource = resource FileTest.stubs(:file?).with('/etc/rc.common').returns true FileTest.stubs(:executable?).with('/etc/rc.common').returns true # All OpenWrt tests operate on the init script directly. It must exist. File.stubs(:directory?).with('/etc/init.d').returns true Puppet::FileSystem.stubs(:exist?).with('/etc/init.d/myservice').returns true FileTest.stubs(:file?).with('/etc/init.d/myservice').returns true FileTest.stubs(:executable?).with('/etc/init.d/myservice').returns true end operatingsystem = 'openwrt' it "should be the default provider on #{operatingsystem}" do Facter.expects(:value).with(:operatingsystem).returns(operatingsystem) described_class.default?.should be_true end # test self.instances describe "when getting all service instances" do let(:services) {['dnsmasq', 'dropbear', 'firewall', 'led', 'puppet', 'uhttpd' ]} before :each do Dir.stubs(:entries).returns services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) end it "should return instances for all services" do services.each do |inst| described_class.expects(:new).with{|hash| hash[:name] == inst && hash[:path] == '/etc/init.d'}.returns("#{inst}_instance") end results = services.collect {|x| "#{x}_instance"} described_class.instances.should == results end end it "should have an enabled? method" do provider.should respond_to(:enabled?) end it "should have an enable method" do provider.should respond_to(:enable) end it "should have a disable method" do provider.should respond_to(:disable) end [:start, :stop, :restart].each do |method| it "should have a #{method} method" do provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do resource.stubs(:[]).with(method).returns "/user/specified/command" provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } provider.send(method) end it "should execute the init script with #{method} when no explicit command is provided" do resource.stubs(:[]).with("has#{method}".intern).returns :true provider.expects(:execute).with { |command, *args| command == ['/etc/init.d/myservice', method ]} provider.send(method) end end end describe "when checking status" do it "should consider the service :running if it has a pid" do provider.expects(:getpid).returns "1234" provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do provider.expects(:getpid).returns nil provider.status.should == :stopped end end end diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb index 108a28593..9478cd83b 100755 --- a/spec/unit/provider/service/redhat_spec.rb +++ b/spec/unit/provider/service/redhat_spec.rb @@ -1,163 +1,163 @@ #! /usr/bin/env ruby # # Unit testing for the RedHat service Provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:redhat) -describe provider_class, :as_platform => :posix do +describe provider_class, :if => Puppet.features.posix? do before :each do @class = Puppet::Type.type(:service).provider(:redhat) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @provider = provider_class.new @resource.stubs(:provider).returns @provider @provider.resource = @resource @provider.stubs(:get).with(:hasstatus).returns false FileTest.stubs(:file?).with('/sbin/service').returns true FileTest.stubs(:executable?).with('/sbin/service').returns true Facter.stubs(:value).with(:operatingsystem).returns('CentOS') end osfamily = [ 'RedHat', 'Suse' ] osfamily.each do |osfamily| it "should be the default provider on #{osfamily}" do Facter.expects(:value).with(:osfamily).returns(osfamily) provider_class.default?.should be_true end end # test self.instances describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four', 'kudzu', 'functions', 'halt', 'killall', 'single', 'linuxconf', 'boot', 'reboot'] @not_services = ['functions', 'halt', 'killall', 'single', 'linuxconf', 'reboot', 'boot'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) end it "should return instances for all services" do (@services-@not_services).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:path] == '/etc/init.d'}.returns("#{inst}_instance") end results = (@services-@not_services).collect {|x| "#{x}_instance"} @class.instances.should == results end it "should call service status when initialized from provider" do @resource.stubs(:[]).with(:status).returns nil @provider.stubs(:get).with(:hasstatus).returns true @provider.expects(:execute).with{|command, *args| command == ['/sbin/service', 'myservice', 'status']} @provider.send(:status) end end it "should use '--add' and 'on' when calling enable" do provider_class.expects(:chkconfig).with("--add", @resource[:name]) provider_class.expects(:chkconfig).with(@resource[:name], :on) @provider.enable end it "(#15797) should explicitly turn off the service in all run levels" do provider_class.expects(:chkconfig).with("--level", "0123456", @resource[:name], :off) @provider.disable end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end describe "when checking enabled? on Suse" do before :each do Facter.expects(:value).with(:osfamily).returns 'Suse' end it "should check for on" do provider_class.stubs(:chkconfig).with(@resource[:name]).returns "#{@resource[:name]} on" @provider.enabled?.should == :true end it "should check for off" do provider_class.stubs(:chkconfig).with(@resource[:name]).returns "#{@resource[:name]} off" @provider.enabled?.should == :false end it "should check for unknown service" do provider_class.stubs(:chkconfig).with(@resource[:name]).returns "#{@resource[:name]}: unknown service" @provider.enabled?.should == :false end end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should execute the service script with #{method} when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ['/sbin/service', 'myservice', method.to_s]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the service script with fail_on_failure false" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process with the server script" do @provider.expects(:texecute).with(:stop, ['/sbin/service', 'myservice', 'stop'], true) @provider.expects(:texecute).with(:start, ['/sbin/service', 'myservice', 'start'], true) @provider.restart end end end diff --git a/spec/unit/provider/service/smf_spec.rb b/spec/unit/provider/service/smf_spec.rb index 2674f76e9..99fba15cb 100755 --- a/spec/unit/provider/service/smf_spec.rb +++ b/spec/unit/provider/service/smf_spec.rb @@ -1,153 +1,153 @@ #! /usr/bin/env ruby # # Unit testing for the SMF service Provider # # author Dominic Cleal # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:smf) -describe provider_class, :as_platform => :posix do +describe provider_class, :if => Puppet.features.posix? do before(:each) do # Create a mock resource @resource = Puppet::Type.type(:service).new( :name => "/system/myservice", :ensure => :running, :enable => :true) @provider = provider_class.new(@resource) FileTest.stubs(:file?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:executable?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:file?).with('/usr/bin/svcs').returns true FileTest.stubs(:executable?).with('/usr/bin/svcs').returns true end describe ".instances" do it "should have an instances method" do provider_class.should respond_to :instances end it "should get a list of services (excluding legacy)" do provider_class.expects(:svcs).with().returns File.read(my_fixture('svcs.out')) instances = provider_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure)} } # we dont manage legacy instances.size.should == 2 instances[0].should == {:name => 'svc:/system/svc/restarter:default', :ensure => :running } instances[1].should == {:name => 'svc:/network/cswrsyncd:default', :ensure => :maintenance } end end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when checking status" do it "should call the external command 'svcs /system/myservice' once" do @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.status end it "should return stopped if svcs can't find the service" do @provider.stubs(:svcs).raises(Puppet::ExecutionFailure.new("no svc found")) @provider.status.should == :stopped end it "should return running if online in svcs output" do @provider.stubs(:svcs).returns("online\t-") @provider.status.should == :running end it "should return stopped if disabled in svcs output" do @provider.stubs(:svcs).returns("disabled\t-") @provider.status.should == :stopped end it "should return maintenance if in maintenance in svcs output" do @provider.stubs(:svcs).returns("maintenance\t-") @provider.status.should == :maintenance end it "should return target state if transitioning in svcs output" do @provider.stubs(:svcs).returns("online\tdisabled") @provider.status.should == :stopped end it "should throw error if it's a legacy service in svcs output" do @provider.stubs(:svcs).returns("legacy_run\t-") lambda { @provider.status }.should raise_error(Puppet::Error, "Cannot manage legacy services through SMF") end end describe "when starting" do it "should enable the service if it is not enabled" do @provider.expects(:status).returns :stopped @provider.expects(:texecute) @provider.start end it "should always execute external command 'svcadm enable /system/myservice'" do @provider.stubs(:status).returns :running @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.start end it "should execute external command 'svcadm clear /system/myservice' if in maintenance" do @provider.stubs(:status).returns :maintenance @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :clear, "/system/myservice"], true) @provider.start end end describe "when starting a service with a manifest" do before(:each) do @resource = Puppet::Type.type(:service).new(:name => "/system/myservice", :ensure => :running, :enable => :true, :manifest => "/tmp/myservice.xml") @provider = provider_class.new(@resource) $CHILD_STATUS.stubs(:exitstatus).returns(1) end it "should import the manifest if service is missing" do @provider.expects(:svccfg).with(:import, "/tmp/myservice.xml") @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.start end it "should handle failures if importing a manifest" do @provider.expects(:svccfg).raises(Puppet::ExecutionFailure.new("can't svccfg import")) lambda { @provider.start }.should raise_error(Puppet::Error, "Cannot config /system/myservice to enable it: can't svccfg import") end end describe "when stopping" do it "should execute external command 'svcadm disable /system/myservice'" do @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "-s", "/system/myservice"], true) @provider.stop end end describe "when restarting" do it "should call 'svcadm restart /system/myservice'" do @provider.expects(:texecute).with(:restart, ["/usr/sbin/svcadm", :restart, "/system/myservice"], true) @provider.restart end end end diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index ec9847e91..681cae429 100755 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -1,772 +1,772 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:exec) do include PuppetSpec::Files def exec_tester(command, exitstatus = 0, rest = {}) Puppet.features.stubs(:root?).returns(true) output = rest.delete(:output) || '' output = Puppet::Util::Execution::ProcessOutput.new(output, exitstatus) tries = rest[:tries] || 1 args = { :name => command, :path => @example_path, :logoutput => false, :loglevel => :err, :returns => 0 }.merge(rest) exec = Puppet::Type.type(:exec).new(args) status = stub "process", :exitstatus => exitstatus Puppet::Util::Execution.expects(:execute).times(tries). with() { |*args| args[0] == command && args[1][:override_locale] == false && args[1].has_key?(:custom_environment) }.returns(output) return exec end before do @command = make_absolute('/bin/true whatever') @executable = make_absolute('/bin/true') @bogus_cmd = make_absolute('/bogus/cmd') end describe "when not stubbing the provider" do before do path = tmpdir('path') ext = Puppet.features.microsoft_windows? ? '.exe' : '' true_cmd = File.join(path, "true#{ext}") false_cmd = File.join(path, "false#{ext}") FileUtils.touch(true_cmd) FileUtils.touch(false_cmd) File.chmod(0755, true_cmd) File.chmod(0755, false_cmd) @example_path = [path] end it "should return :executed_command as its event" do resource = Puppet::Type.type(:exec).new :command => @command resource.parameter(:returns).event.name.should == :executed_command end describe "when execing" do it "should use the 'execute' method to exec" do exec_tester("true").refresh.should == :executed_command end it "should report a failure" do expect { exec_tester('false', 1).refresh }. to raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should not report a failure if the exit status is specified in a returns array" do expect { exec_tester("false", 1, :returns => [0, 1]).refresh }.to_not raise_error end it "should report a failure if the exit status is not specified in a returns array" do expect { exec_tester('false', 1, :returns => [0, 100]).refresh }. to raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should log the output on success" do output = "output1\noutput2\n" exec_tester('false', 0, :output => output, :logoutput => true).refresh output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure" do output = "output1\noutput2\n" expect { exec_tester('false', 1, :output => output, :logoutput => true).refresh }. to raise_error(Puppet::Error) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end end describe "when logoutput=>on_failure is set" do it "should log the output on failure" do output = "output1\noutput2\n" expect { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }. to raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure when returns is specified as an array" do output = "output1\noutput2\n" expect { exec_tester('false', 1, :output => output, :returns => [0, 100], :logoutput => :on_failure).refresh }.to raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "shouldn't log the output on success" do exec_tester('true', 0, :output => "a\nb\nc\n", :logoutput => :on_failure).refresh @logs.should == [] end end it "shouldn't log the output on success when non-zero exit status is in a returns array" do exec_tester("true", 100, :output => "a\n", :logoutput => :on_failure, :returns => [1, 100]).refresh @logs.should == [] end describe " when multiple tries are set," do it "should repeat the command attempt 'tries' times on failure and produce an error" do tries = 5 resource = exec_tester("false", 1, :tries => tries, :try_sleep => 0) expect { resource.refresh }.to raise_error(Puppet::Error) end end end it "should be able to autorequire files mentioned in the command" do foo = make_absolute('/bin/foo') catalog = Puppet::Resource::Catalog.new tmp = Puppet::Type.type(:file).new(:name => foo) execer = Puppet::Type.type(:exec).new(:name => foo) catalog.add_resource tmp catalog.add_resource execer dependencies = execer.autorequire(catalog) dependencies.collect(&:to_s).should == [Puppet::Relationship.new(tmp, execer).to_s] end describe "when handling the path parameter" do expect = %w{one two three four} { "an array" => expect, "a path-separator delimited list" => expect.join(File::PATH_SEPARATOR), "both array and path-separator delimited lists" => ["one", "two#{File::PATH_SEPARATOR}three", "four"], }.each do |test, input| it "should accept #{test}" do type = Puppet::Type.type(:exec).new(:name => @command, :path => input) type[:path].should == expect end end describe "on platforms where path separator is not :" do before :each do @old_verbosity = $VERBOSE $VERBOSE = nil @old_separator = File::PATH_SEPARATOR File::PATH_SEPARATOR = 'q' end after :each do File::PATH_SEPARATOR = @old_separator $VERBOSE = @old_verbosity end it "should use the path separator of the current platform" do type = Puppet::Type.type(:exec).new(:name => @command, :path => "fooqbarqbaz") type[:path].should == %w[foo bar baz] end end end describe "when setting user" do - describe "on POSIX systems", :as_platform => :posix do + describe "on POSIX systems", :if => Puppet.features.posix? do it "should fail if we are not root" do Puppet.features.stubs(:root?).returns(false) expect { Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input') }.to raise_error Puppet::Error, /Parameter user failed/ end it "accepts the current user" do Puppet.features.stubs(:root?).returns(false) Etc.stubs(:getpwuid).returns(Struct::Passwd.new('input')) type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input') expect(type[:user]).to eq('input') end ['one', 2, 'root', 4294967295, 4294967296].each do |value| it "should accept '#{value}' as user if we are root" do Puppet.features.stubs(:root?).returns(true) type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => value) type[:user].should == value end end end - describe "on Windows systems", :as_platform => :windows do + describe "on Windows systems", :if => Puppet.features.microsoft_windows? do before :each do Puppet.features.stubs(:root?).returns(true) end it "should reject user parameter" do expect { Puppet::Type.type(:exec).new(:name => 'c:\windows\notepad.exe', :user => 'input') }.to raise_error Puppet::Error, /Unable to execute commands as other users on Windows/ end end end describe "when setting group" do shared_examples_for "exec[:group]" do ['one', 2, 'wheel', 4294967295, 4294967296].each do |value| it "should accept '#{value}' without error or judgement" do type = Puppet::Type.type(:exec).new(:name => @command, :group => value) type[:group].should == value end end end describe "when running as root" do before :each do Puppet.features.stubs(:root?).returns(true) end it_behaves_like "exec[:group]" end describe "when not running as root" do before :each do Puppet.features.stubs(:root?).returns(false) end it_behaves_like "exec[:group]" end end describe "when setting cwd" do it_should_behave_like "all path parameters", :cwd, :array => false do def instance(path) # Specify shell provider so we don't have to care about command validation Puppet::Type.type(:exec).new(:name => @executable, :cwd => path, :provider => :shell) end end end shared_examples_for "all exec command parameters" do |param| { "relative" => "example", "absolute" => "/bin/example" }.sort.each do |name, command| describe "if command is #{name}" do before :each do @param = param end def test(command, valid) if @param == :name then instance = Puppet::Type.type(:exec).new() else instance = Puppet::Type.type(:exec).new(:name => @executable) end if valid then instance.provider.expects(:validatecmd).returns(true) else instance.provider.expects(:validatecmd).raises(Puppet::Error, "from a stub") end instance[@param] = command end it "should work if the provider calls the command valid" do expect { test(command, true) }.to_not raise_error end it "should fail if the provider calls the command invalid" do expect { test(command, false) }. to raise_error Puppet::Error, /Parameter #{@param} failed on Exec\[.*\]: from a stub/ end end end end shared_examples_for "all exec command parameters that take arrays" do |param| describe "when given an array of inputs" do before :each do @test = Puppet::Type.type(:exec).new(:name => @executable) end it "should accept the array when all commands return valid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(true) @test[param] = input @test[param].should == input end it "should reject the array when any commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).with(input.first).returns(false) input[1..-1].each do |cmd| @test.provider.expects(:validatecmd).with(cmd).returns(true) end @test[param] = input @test[param].should == input end it "should reject the array when all commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(false) @test[param] = input @test[param].should == input end end end describe "when setting command" do subject { described_class.new(:name => @command) } it "fails when passed an Array" do expect { subject[:command] = [] }.to raise_error Puppet::Error, /Command must be a String/ end it "fails when passed a Hash" do expect { subject[:command] = {} }.to raise_error Puppet::Error, /Command must be a String/ end end describe "when setting refresh" do it_should_behave_like "all exec command parameters", :refresh end describe "for simple parameters" do before :each do @exec = Puppet::Type.type(:exec).new(:name => @executable) end describe "when setting environment" do { "single values" => "foo=bar", "multiple values" => ["foo=bar", "baz=quux"], }.each do |name, data| it "should accept #{name}" do @exec[:environment] = data @exec[:environment].should == data end end { "single values" => "foo", "only values" => ["foo", "bar"], "any values" => ["foo=bar", "baz"] }.each do |name, data| it "should reject #{name} without assignment" do expect { @exec[:environment] = data }. to raise_error Puppet::Error, /Invalid environment setting/ end end end describe "when setting timeout" do [0, 0.1, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:timeout] = valid @exec[:timeout].should == valid end it "should accept '#{valid}' in an array as valid" do @exec[:timeout] = [valid] @exec[:timeout].should == valid end end ['1/2', '', 'foo', '5foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:timeout] = invalid }. to raise_error Puppet::Error, /The timeout must be a number/ end it "should reject '#{invalid}' in an array as invalid" do expect { @exec[:timeout] = [invalid] }. to raise_error Puppet::Error, /The timeout must be a number/ end end it "should fail if timeout is exceeded" do ruby_path = Puppet::Util::Execution.ruby_path() ## Leaving this commented version in here because it fails on windows, due to what appears to be ## an assumption about hash iteration order in lib/puppet/type.rb#hash2resource, where ## resource[]= will overwrite the namevar with ":name" if the iteration is in the wrong order #sleep_exec = Puppet::Type.type(:exec).new(:name => 'exec_spec sleep command', :command => "#{ruby_path} -e 'sleep 0.02'", :timeout => '0.01') sleep_exec = Puppet::Type.type(:exec).new(:name => "#{ruby_path} -e 'sleep 0.02'", :timeout => '0.01') expect { sleep_exec.refresh }.to raise_error Puppet::Error, "Command exceeded timeout" end it "should convert timeout to a float" do command = make_absolute('/bin/false') resource = Puppet::Type.type(:exec).new :command => command, :timeout => "12" resource[:timeout].should be_a(Float) resource[:timeout].should == 12.0 end it "should munge negative timeouts to 0.0" do command = make_absolute('/bin/false') resource = Puppet::Type.type(:exec).new :command => command, :timeout => "-12.0" resource.parameter(:timeout).value.should be_a(Float) resource.parameter(:timeout).value.should == 0.0 end end describe "when setting tries" do [1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:tries] = valid @exec[:tries].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:tries] = [valid] @exec[:tries].should == valid end end end [-3.5, -1, 0, 0.2, '1/2', '1_000_000', '+12', '', 'foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:tries] = invalid }. to raise_error Puppet::Error, /Tries must be an integer/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:tries] = [invalid] }. to raise_error Puppet::Error, /Tries must be an integer/ end end end end describe "when setting try_sleep" do [0, 0.2, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:try_sleep] = valid @exec[:try_sleep].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:try_sleep] = [valid] @exec[:try_sleep].should == valid end end end { -3.5 => "cannot be a negative number", -1 => "cannot be a negative number", '1/2' => 'must be a number', '1_000_000' => 'must be a number', '+12' => 'must be a number', '' => 'must be a number', 'foo' => 'must be a number', }.each do |invalid, error| it "should reject '#{invalid}' as invalid" do expect { @exec[:try_sleep] = invalid }. to raise_error Puppet::Error, /try_sleep #{error}/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:try_sleep] = [invalid] }. to raise_error Puppet::Error, /try_sleep #{error}/ end end end end describe "when setting refreshonly" do [:true, :false].each do |value| it "should accept '#{value}'" do @exec[:refreshonly] = value @exec[:refreshonly].should == value end end [1, 0, "1", "0", "yes", "y", "no", "n"].each do |value| it "should reject '#{value}'" do expect { @exec[:refreshonly] = value }. to raise_error(Puppet::Error, /Invalid value #{value.inspect}\. Valid values are true, false/ ) end end end end describe "when setting creates" do it_should_behave_like "all path parameters", :creates, :array => true do def instance(path) # Specify shell provider so we don't have to care about command validation Puppet::Type.type(:exec).new(:name => @executable, :creates => path, :provider => :shell) end end end describe "when setting unless" do it_should_behave_like "all exec command parameters", :unless it_should_behave_like "all exec command parameters that take arrays", :unless end describe "when setting onlyif" do it_should_behave_like "all exec command parameters", :onlyif it_should_behave_like "all exec command parameters that take arrays", :onlyif end describe "#check" do before :each do @test = Puppet::Type.type(:exec).new(:name => @executable) end describe ":refreshonly" do { :true => false, :false => true }.each do |input, result| it "should return '#{result}' when given '#{input}'" do @test[:refreshonly] = input @test.check_all_attributes.should == result end end end describe ":creates" do before :each do @exist = tmpfile('exist') FileUtils.touch(@exist) @unexist = tmpfile('unexist') end context "with a single item" do it "should run when the item does not exist" do @test[:creates] = @unexist @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = @exist @test.check_all_attributes.should == false end end context "with an array with one item" do it "should run when the item does not exist" do @test[:creates] = [@unexist] @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = [@exist] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run when all items do not exist" do @test[:creates] = [@unexist] * 3 @test.check_all_attributes.should == true end it "should not run when one item exists" do @test[:creates] = [@unexist, @exist, @unexist] @test.check_all_attributes.should == false end it "should not run when all items exist" do @test[:creates] = [@exist] * 3 end end end { :onlyif => { :pass => false, :fail => true }, :unless => { :pass => true, :fail => false }, }.each do |param, sense| describe ":#{param}" do before :each do @pass = make_absolute("/magic/pass") @fail = make_absolute("/magic/fail") @pass_status = stub('status', :exitstatus => sense[:pass] ? 0 : 1) @fail_status = stub('status', :exitstatus => sense[:fail] ? 0 : 1) @test.provider.stubs(:checkexe).returns(true) [true, false].each do |check| @test.provider.stubs(:run).with(@pass, check). returns(['test output', @pass_status]) @test.provider.stubs(:run).with(@fail, check). returns(['test output', @fail_status]) end end context "with a single item" do it "should run if the command exits non-zero" do @test[param] = @fail @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = @pass @test.check_all_attributes.should == false end end context "with an array with a single item" do it "should run if the command exits non-zero" do @test[param] = [@fail] @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = [@pass] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run if all the commands exits non-zero" do @test[param] = [@fail] * 3 @test.check_all_attributes.should == true end it "should not run if one command exits zero" do @test[param] = [@pass, @fail, @pass] @test.check_all_attributes.should == false end it "should not run if all command exits zero" do @test[param] = [@pass] * 3 @test.check_all_attributes.should == false end end it "should emit output to debug" do Puppet::Util::Log.level = :debug @test[param] = @fail @test.check_all_attributes.should == true @logs.shift.message.should == "test output" end end end end describe "#retrieve" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return :notrun when check_all_attributes returns true" do @exec_resource.stubs(:check_all_attributes).returns true @exec_resource.retrieve[:returns].should == :notrun end it "should return default exit code 0 when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource.retrieve[:returns].should == ['0'] end it "should return the specified exit code when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource[:returns] = 42 @exec_resource.retrieve[:returns].should == ["42"] end end describe "#output" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return the provider's run output" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh @exec_resource.output.should == 'silly output' end end describe "#refresh" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should call provider run with the refresh parameter if it is set" do myother_bogus_cmd = make_absolute('/myother/bogus/cmd') provider = stub 'provider' @exec_resource.stubs(:provider).returns(provider) @exec_resource.stubs(:[]).with(:refresh).returns(myother_bogus_cmd) provider.expects(:run).with(myother_bogus_cmd) @exec_resource.refresh end it "should call provider run with the specified command if the refresh parameter is not set" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).with(@bogus_cmd).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end it "should not run the provider if check_all_attributes is false" do @exec_resource.stubs(:check_all_attributes).returns false provider = stub 'provider' provider.expects(:run).never @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end end describe "relative and absolute commands vs path" do let :type do Puppet::Type.type(:exec) end let :rel do 'echo' end let :abs do make_absolute('/bin/echo') end let :path do make_absolute('/bin') end it "should fail with relative command and no path" do expect { type.new(:command => rel) }. to raise_error Puppet::Error, /no path was specified/ end it "should accept a relative command with a path" do type.new(:command => rel, :path => path).must be end it "should accept an absolute command with no path" do type.new(:command => abs).must be end it "should accept an absolute command with a path" do type.new(:command => abs, :path => path).must be end end describe "when providing a umask" do it "should fail if an invalid umask is used" do resource = Puppet::Type.type(:exec).new :command => @command expect { resource[:umask] = '0028'}.to raise_error(Puppet::ResourceError, /umask specification is invalid/) expect { resource[:umask] = '28' }.to raise_error(Puppet::ResourceError, /umask specification is invalid/) end end end diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb index 0e95566a3..e48972a62 100755 --- a/spec/unit/util/log/destinations_spec.rb +++ b/spec/unit/util/log/destinations_spec.rb @@ -1,236 +1,236 @@ #! /usr/bin/env ruby require 'spec_helper' require 'json' require 'puppet/util/log' describe Puppet::Util::Log.desttypes[:report] do before do @dest = Puppet::Util::Log.desttypes[:report] end it "should require a report at initialization" do @dest.new("foo").report.should == "foo" end it "should send new messages to the report" do report = mock 'report' dest = @dest.new(report) report.expects(:<<).with("my log") dest.handle "my log" end end describe Puppet::Util::Log.desttypes[:file] do include PuppetSpec::Files before do File.stubs(:open) # prevent actually creating the file File.stubs(:chown) # prevent chown on non existing file from failing @class = Puppet::Util::Log.desttypes[:file] end it "should default to autoflush false" do @class.new(tmpfile('log')).autoflush.should == true end describe "when matching" do shared_examples_for "file destination" do it "should match an absolute path" do @class.match?(abspath).should be_true end it "should not match a relative path" do @class.match?(relpath).should be_false end end - describe "on POSIX systems", :as_platform => :posix do + describe "on POSIX systems", :if => Puppet.features.posix? do let (:abspath) { '/tmp/log' } let (:relpath) { 'log' } it_behaves_like "file destination" it "logs an error if it can't chown the file owner & group" do FileUtils.expects(:chown).with(Puppet[:user], Puppet[:group], abspath).raises(Errno::EPERM) Puppet.features.expects(:root?).returns(true) Puppet.expects(:err).with("Unable to set ownership to #{Puppet[:user]}:#{Puppet[:group]} for log file: #{abspath}") @class.new(abspath) end it "doesn't attempt to chown when running as non-root" do FileUtils.expects(:chown).with(Puppet[:user], Puppet[:group], abspath).never Puppet.features.expects(:root?).returns(false) @class.new(abspath) end end - describe "on Windows systems", :as_platform => :windows do + describe "on Windows systems", :if => Puppet.features.microsoft_windows? do let (:abspath) { 'C:\\temp\\log.txt' } let (:relpath) { 'log.txt' } it_behaves_like "file destination" end end end describe Puppet::Util::Log.desttypes[:syslog] do let (:klass) { Puppet::Util::Log.desttypes[:syslog] } # these tests can only be run when syslog is present, because # we can't stub the top-level Syslog module describe "when syslog is available", :if => Puppet.features.syslog? do before :each do Syslog.stubs(:opened?).returns(false) Syslog.stubs(:const_get).returns("LOG_KERN").returns(0) Syslog.stubs(:open) end it "should open syslog" do Syslog.expects(:open) klass.new end it "should close syslog" do Syslog.expects(:close) dest = klass.new dest.close end it "should send messages to syslog" do syslog = mock 'syslog' syslog.expects(:info).with("don't panic") Syslog.stubs(:open).returns(syslog) msg = Puppet::Util::Log.new(:level => :info, :message => "don't panic") dest = klass.new dest.handle(msg) end end describe "when syslog is unavailable" do it "should not be a suitable log destination" do Puppet.features.stubs(:syslog?).returns(false) klass.suitable?(:syslog).should be_false end end end describe Puppet::Util::Log.desttypes[:logstash_event] do describe "when using structured log format with logstash_event schema" do before :each do @msg = Puppet::Util::Log.new(:level => :info, :message => "So long, and thanks for all the fish.", :source => "a dolphin") end it "format should fix the hash to have the correct structure" do dest = described_class.new result = dest.format(@msg) result["version"].should == 1 result["level"].should == :info result["message"].should == "So long, and thanks for all the fish." result["source"].should == "a dolphin" # timestamp should be within 10 seconds Time.parse(result["@timestamp"]).should >= ( Time.now - 10 ) end it "format returns a structure that can be converted to json" do dest = described_class.new hash = dest.format(@msg) JSON.parse(hash.to_json) end it "handle should send the output to stdout" do $stdout.expects(:puts).once dest = described_class.new dest.handle(@msg) end end end describe Puppet::Util::Log.desttypes[:console] do let (:klass) { Puppet::Util::Log.desttypes[:console] } describe "when color is available" do before :each do subject.stubs(:console_has_color?).returns(true) end it "should support color output" do Puppet[:color] = true subject.colorize(:red, 'version').should == "\e[0;31mversion\e[0m" end it "should withhold color output when not appropriate" do Puppet[:color] = false subject.colorize(:red, 'version').should == "version" end it "should handle multiple overlapping colors in a stack-like way" do Puppet[:color] = true vstring = subject.colorize(:red, 'version') subject.colorize(:green, "(#{vstring})").should == "\e[0;32m(\e[0;31mversion\e[0;32m)\e[0m" end it "should handle resets in a stack-like way" do Puppet[:color] = true vstring = subject.colorize(:reset, 'version') subject.colorize(:green, "(#{vstring})").should == "\e[0;32m(\e[mversion\e[0;32m)\e[0m" end it "should include the log message's source/context in the output when available" do Puppet[:color] = false $stdout.expects(:puts).with("Info: a hitchhiker: don't panic") msg = Puppet::Util::Log.new(:level => :info, :message => "don't panic", :source => "a hitchhiker") dest = klass.new dest.handle(msg) end end end describe ":eventlog", :if => Puppet::Util::Platform.windows? do let(:klass) { Puppet::Util::Log.desttypes[:eventlog] } def expects_message_with_type(klass, level, eventlog_type, eventlog_id) eventlog = stub('eventlog') eventlog.expects(:report_event).with(has_entries(:source => "Puppet", :event_type => eventlog_type, :event_id => eventlog_id, :data => "a hitchhiker: don't panic")) Win32::EventLog.stubs(:open).returns(eventlog) msg = Puppet::Util::Log.new(:level => level, :message => "don't panic", :source => "a hitchhiker") dest = klass.new dest.handle(msg) end it "supports the eventlog feature" do expect(Puppet.features.eventlog?).to be_true end it "logs to the Application event log" do eventlog = stub('eventlog') Win32::EventLog.expects(:open).with('Application').returns(stub('eventlog')) klass.new end it "logs :debug level as an information type event" do expects_message_with_type(klass, :debug, klass::EVENTLOG_INFORMATION_TYPE, 0x1) end it "logs :warning level as an warning type event" do expects_message_with_type(klass, :warning, klass::EVENTLOG_WARNING_TYPE, 0x2) end it "logs :err level as an error type event" do expects_message_with_type(klass, :err, klass::EVENTLOG_ERROR_TYPE, 0x3) end end diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb index e22ffb24a..d59e78a52 100755 --- a/spec/unit/util_spec.rb +++ b/spec/unit/util_spec.rb @@ -1,545 +1,545 @@ #!/usr/bin/env ruby require 'spec_helper' describe Puppet::Util do include PuppetSpec::Files if Puppet.features.microsoft_windows? def set_mode(mode, file) Puppet::Util::Windows::Security.set_mode(mode, file) end def get_mode(file) Puppet::Util::Windows::Security.get_mode(file) & 07777 end else def set_mode(mode, file) File.chmod(mode, file) end def get_mode(file) Puppet::FileSystem.lstat(file).mode & 07777 end end describe "#withenv" do before :each do @original_path = ENV["PATH"] @new_env = {:PATH => "/some/bogus/path"} end it "should change environment variables within the block then reset environment variables to their original values" do Puppet::Util.withenv @new_env do ENV["PATH"].should == "/some/bogus/path" end ENV["PATH"].should == @original_path end it "should reset environment variables to their original values even if the block fails" do begin Puppet::Util.withenv @new_env do ENV["PATH"].should == "/some/bogus/path" raise "This is a failure" end rescue end ENV["PATH"].should == @original_path end it "should reset environment variables even when they are set twice" do # Setting Path & Environment parameters in Exec type can cause weirdness @new_env["PATH"] = "/someother/bogus/path" Puppet::Util.withenv @new_env do # When assigning duplicate keys, can't guarantee order of evaluation ENV["PATH"].should =~ /\/some.*\/bogus\/path/ end ENV["PATH"].should == @original_path end it "should remove any new environment variables after the block ends" do @new_env[:FOO] = "bar" ENV["FOO"] = nil Puppet::Util.withenv @new_env do ENV["FOO"].should == "bar" end ENV["FOO"].should == nil end end describe "#absolute_path?" do - describe "on posix systems", :as_platform => :posix do + describe "on posix systems", :if => Puppet.features.posix? do it "should default to the platform of the local system" do Puppet::Util.should be_absolute_path('/foo') Puppet::Util.should_not be_absolute_path('C:/foo') end end - describe "on windows", :as_platform => :windows do + describe "on windows", :if => Puppet.features.microsoft_windows? do it "should default to the platform of the local system" do Puppet::Util.should be_absolute_path('C:/foo') Puppet::Util.should_not be_absolute_path('/foo') end end describe "when using platform :posix" do %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path| it "should return true for #{path}" do Puppet::Util.should be_absolute_path(path, :posix) end end %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path| it "should return false for #{path}" do Puppet::Util.should_not be_absolute_path(path, :posix) end end end describe "when using platform :windows" do %w[C:/foo C:\foo \\\\Server\Foo\Bar \\\\?\C:\foo\bar //Server/Foo/Bar //?/C:/foo/bar /\?\C:/foo\bar \/Server\Foo/Bar c:/foo//bar//baz].each do |path| it "should return true for #{path}" do Puppet::Util.should be_absolute_path(path, :windows) end end %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path| it "should return false for #{path}" do Puppet::Util.should_not be_absolute_path(path, :windows) end end end end describe "#path_to_uri" do %w[. .. foo foo/bar foo/../bar].each do |path| it "should reject relative path: #{path}" do lambda { Puppet::Util.path_to_uri(path) }.should raise_error(Puppet::Error) end end it "should perform URI escaping" do Puppet::Util.path_to_uri("/foo bar").path.should == "/foo%20bar" end describe "when using platform :posix" do before :each do Puppet.features.stubs(:posix).returns true Puppet.features.stubs(:microsoft_windows?).returns false end %w[/ /foo /foo/../bar].each do |path| it "should convert #{path} to URI" do Puppet::Util.path_to_uri(path).path.should == path end end end describe "when using platform :windows" do before :each do Puppet.features.stubs(:posix).returns false Puppet.features.stubs(:microsoft_windows?).returns true end it "should normalize backslashes" do Puppet::Util.path_to_uri('c:\\foo\\bar\\baz').path.should == '/' + 'c:/foo/bar/baz' end %w[C:/ C:/foo/bar].each do |path| it "should convert #{path} to absolute URI" do Puppet::Util.path_to_uri(path).path.should == '/' + path end end %w[share C$].each do |path| it "should convert UNC #{path} to absolute URI" do uri = Puppet::Util.path_to_uri("\\\\server\\#{path}") uri.host.should == 'server' uri.path.should == '/' + path end end end end describe ".uri_to_path" do require 'uri' it "should strip host component" do Puppet::Util.uri_to_path(URI.parse('http://foo/bar')).should == '/bar' end it "should accept puppet URLs" do Puppet::Util.uri_to_path(URI.parse('puppet:///modules/foo')).should == '/modules/foo' end it "should return unencoded path" do Puppet::Util.uri_to_path(URI.parse('http://foo/bar%20baz')).should == '/bar baz' end it "should be nil-safe" do Puppet::Util.uri_to_path(nil).should be_nil end describe "when using platform :posix",:if => Puppet.features.posix? do it "should accept root" do Puppet::Util.uri_to_path(URI.parse('file:/')).should == '/' end it "should accept single slash" do Puppet::Util.uri_to_path(URI.parse('file:/foo/bar')).should == '/foo/bar' end it "should accept triple slashes" do Puppet::Util.uri_to_path(URI.parse('file:///foo/bar')).should == '/foo/bar' end end describe "when using platform :windows", :if => Puppet.features.microsoft_windows? do it "should accept root" do Puppet::Util.uri_to_path(URI.parse('file:/C:/')).should == 'C:/' end it "should accept single slash" do Puppet::Util.uri_to_path(URI.parse('file:/C:/foo/bar')).should == 'C:/foo/bar' end it "should accept triple slashes" do Puppet::Util.uri_to_path(URI.parse('file:///C:/foo/bar')).should == 'C:/foo/bar' end it "should accept file scheme with double slashes as a UNC path" do Puppet::Util.uri_to_path(URI.parse('file://host/share/file')).should == '//host/share/file' end end end describe "safe_posix_fork" do let(:pid) { 5501 } before :each do # Most of the things this method does are bad to do during specs. :/ Kernel.stubs(:fork).returns(pid).yields $stdin.stubs(:reopen) $stdout.stubs(:reopen) $stderr.stubs(:reopen) # ensure that we don't really close anything! (0..256).each {|n| IO.stubs(:new) } end it "should close all open file descriptors except stdin/stdout/stderr" do # This is ugly, but I can't really think of a better way to do it without # letting it actually close fds, which seems risky (0..2).each {|n| IO.expects(:new).with(n).never} (3..256).each {|n| IO.expects(:new).with(n).returns mock('io', :close) } Puppet::Util.safe_posix_fork end it "should fork a child process to execute the block" do Kernel.expects(:fork).returns(pid).yields Puppet::Util.safe_posix_fork do message = "Fork this!" end end it "should return the pid of the child process" do Puppet::Util.safe_posix_fork.should == pid end end describe "#which" do let(:base) { File.expand_path('/bin') } let(:path) { File.join(base, 'foo') } before :each do FileTest.stubs(:file?).returns false FileTest.stubs(:file?).with(path).returns true FileTest.stubs(:executable?).returns false FileTest.stubs(:executable?).with(path).returns true end it "should accept absolute paths" do Puppet::Util.which(path).should == path end it "should return nil if no executable found" do Puppet::Util.which('doesnotexist').should be_nil end it "should warn if the user's HOME is not set but their PATH contains a ~" do env_path = %w[~/bin /usr/bin /bin].join(File::PATH_SEPARATOR) env = {:HOME => nil, :PATH => env_path} env.merge!({:HOMEDRIVE => nil, :USERPROFILE => nil}) if Puppet.features.microsoft_windows? Puppet::Util.withenv(env) do Puppet::Util::Warnings.expects(:warnonce).once Puppet::Util.which('foo') end end it "should reject directories" do Puppet::Util.which(base).should be_nil end it "should ignore ~user directories if the user doesn't exist" do # Windows treats *any* user as a "user that doesn't exist", which means # that this will work correctly across all our platforms, and should # behave consistently. If they ever implement it correctly (eg: to do # the lookup for real) it should just work transparently. baduser = 'if_this_user_exists_I_will_eat_my_hat' Puppet::Util.withenv("PATH" => "~#{baduser}#{File::PATH_SEPARATOR}#{base}") do Puppet::Util.which('foo').should == path end end describe "on POSIX systems" do before :each do Puppet.features.stubs(:posix?).returns true Puppet.features.stubs(:microsoft_windows?).returns false end it "should walk the search PATH returning the first executable" do ENV.stubs(:[]).with('PATH').returns(File.expand_path('/bin')) Puppet::Util.which('foo').should == path end end describe "on Windows systems" do let(:path) { File.expand_path(File.join(base, 'foo.CMD')) } before :each do Puppet.features.stubs(:posix?).returns false Puppet.features.stubs(:microsoft_windows?).returns true end describe "when a file extension is specified" do it "should walk each directory in PATH ignoring PATHEXT" do ENV.stubs(:[]).with('PATH').returns(%w[/bar /bin].map{|dir| File.expand_path(dir)}.join(File::PATH_SEPARATOR)) FileTest.expects(:file?).with(File.join(File.expand_path('/bar'), 'foo.CMD')).returns false ENV.expects(:[]).with('PATHEXT').never Puppet::Util.which('foo.CMD').should == path end end describe "when a file extension is not specified" do it "should walk each extension in PATHEXT until an executable is found" do bar = File.expand_path('/bar') ENV.stubs(:[]).with('PATH').returns("#{bar}#{File::PATH_SEPARATOR}#{base}") ENV.stubs(:[]).with('PATHEXT').returns(".EXE#{File::PATH_SEPARATOR}.CMD") exts = sequence('extensions') FileTest.expects(:file?).in_sequence(exts).with(File.join(bar, 'foo.EXE')).returns false FileTest.expects(:file?).in_sequence(exts).with(File.join(bar, 'foo.CMD')).returns false FileTest.expects(:file?).in_sequence(exts).with(File.join(base, 'foo.EXE')).returns false FileTest.expects(:file?).in_sequence(exts).with(path).returns true Puppet::Util.which('foo').should == path end it "should walk the default extension path if the environment variable is not defined" do ENV.stubs(:[]).with('PATH').returns(base) ENV.stubs(:[]).with('PATHEXT').returns(nil) exts = sequence('extensions') %w[.COM .EXE .BAT].each do |ext| FileTest.expects(:file?).in_sequence(exts).with(File.join(base, "foo#{ext}")).returns false end FileTest.expects(:file?).in_sequence(exts).with(path).returns true Puppet::Util.which('foo').should == path end it "should fall back if no extension matches" do ENV.stubs(:[]).with('PATH').returns(base) ENV.stubs(:[]).with('PATHEXT').returns(".EXE") FileTest.stubs(:file?).with(File.join(base, 'foo.EXE')).returns false FileTest.stubs(:file?).with(File.join(base, 'foo')).returns true FileTest.stubs(:executable?).with(File.join(base, 'foo')).returns true Puppet::Util.which('foo').should == File.join(base, 'foo') end end end end describe "hash symbolizing functions" do let (:myhash) { { "foo" => "bar", :baz => "bam" } } let (:resulthash) { { :foo => "bar", :baz => "bam" } } describe "#symbolizehash" do it "should return a symbolized hash" do newhash = Puppet::Util.symbolizehash(myhash) newhash.should == resulthash end end end context "#replace_file" do subject { Puppet::Util } it { should respond_to :replace_file } let :target do target = Tempfile.new("puppet-util-replace-file") target.puts("hello, world") target.flush # make sure content is on disk. target.fsync rescue nil target.close target end it "should fail if no block is given" do expect { subject.replace_file(target.path, 0600) }.to raise_error /block/ end it "should replace a file when invoked" do # Check that our file has the expected content. File.read(target.path).should == "hello, world\n" # Replace the file. subject.replace_file(target.path, 0600) do |fh| fh.puts "I am the passenger..." end # ...and check the replacement was complete. File.read(target.path).should == "I am the passenger...\n" end # When running with the same user and group sid, which is the default, # Windows collapses the owner and group modes into a single ACE, resulting # in set(0600) => get(0660) and so forth. --daniel 2012-03-30 modes = [0555, 0660, 0770] modes += [0600, 0700] unless Puppet.features.microsoft_windows? modes.each do |mode| it "should copy 0#{mode.to_s(8)} permissions from the target file by default" do set_mode(mode, target.path) get_mode(target.path).should == mode subject.replace_file(target.path, 0000) {|fh| fh.puts "bazam" } get_mode(target.path).should == mode File.read(target.path).should == "bazam\n" end end it "should copy the permissions of the source file before yielding on Unix", :if => !Puppet.features.microsoft_windows? do set_mode(0555, target.path) inode = Puppet::FileSystem.stat(target.path).ino yielded = false subject.replace_file(target.path, 0600) do |fh| get_mode(fh.path).should == 0555 yielded = true end yielded.should be_true Puppet::FileSystem.stat(target.path).ino.should_not == inode get_mode(target.path).should == 0555 end it "should use the default permissions if the source file doesn't exist" do new_target = target.path + '.foo' Puppet::FileSystem.exist?(new_target).should be_false begin subject.replace_file(new_target, 0555) {|fh| fh.puts "foo" } get_mode(new_target).should == 0555 ensure Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target) end end it "should not replace the file if an exception is thrown in the block" do yielded = false threw = false begin subject.replace_file(target.path, 0600) do |fh| yielded = true fh.puts "different content written, then..." raise "...throw some random failure" end rescue Exception => e if e.to_s =~ /some random failure/ threw = true else raise end end yielded.should be_true threw.should be_true # ...and check the replacement was complete. File.read(target.path).should == "hello, world\n" end {:string => '664', :number => 0664, :symbolic => "ug=rw-,o=r--" }.each do |label,mode| it "should support #{label} format permissions" do new_target = target.path + "#{mode}.foo" Puppet::FileSystem.exist?(new_target).should be_false begin subject.replace_file(new_target, mode) {|fh| fh.puts "this is an interesting content" } get_mode(new_target).should == 0664 ensure Puppet::FileSystem.unlink(new_target) if Puppet::FileSystem.exist?(new_target) end end end end describe "#pretty_backtrace" do it "should include lines that don't match the standard backtrace pattern" do line = "non-standard line\n" trace = caller[0..2] + [line] + caller[3..-1] Puppet::Util.pretty_backtrace(trace).should =~ /#{line}/ end it "should include function names" do Puppet::Util.pretty_backtrace.should =~ /:in `\w+'/ end it "should work with Windows paths" do Puppet::Util.pretty_backtrace(["C:/work/puppet/c.rb:12:in `foo'\n"]). should == "C:/work/puppet/c.rb:12:in `foo'" end end describe "#deterministic_rand" do it "should not fiddle with future rand calls" do Puppet::Util.deterministic_rand(123,20) rand_one = rand() Puppet::Util.deterministic_rand(123,20) rand().should_not eql(rand_one) end if defined?(Random) == 'constant' && Random.class == Class it "should not fiddle with the global seed" do srand(1234) Puppet::Util.deterministic_rand(123,20) srand().should eql(1234) end # ruby below 1.9.2 variant else it "should set a new global seed" do srand(1234) Puppet::Util.deterministic_rand(123,20) srand().should_not eql(1234) end end end end