diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index 0fb412b91..6b1bd0160 100755 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -1,723 +1,747 @@ #!/usr/bin/env rspec 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) || '' 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::SUIDManager.expects(:run_and_capture).times(tries). with() { |*args| args[0] == command && args[1] == nil && args[2] == nil && args[3][:override_locale] == false && args[3].has_key?(:custom_environment) } .returns([output, status]) 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') true_cmd = File.join(path, 'true') false_cmd = File.join(path, 'false') 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 'run_and_capture' method to exec" do exec_tester("true").refresh.should == :executed_command end it "should report a failure" do proc { exec_tester('false', 1).refresh }. should 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 proc { exec_tester("false", 1, :returns => [0, 1]).refresh }.should_not raise_error end it "should report a failure if the exit status is not specified in a returns array" do proc { exec_tester('false', 1, :returns => [0, 100]).refresh }. should 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" proc { exec_tester('false', 1, :output => output, :logoutput => true).refresh }. should 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" proc { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }. should 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" proc { exec_tester('false', 1, :output => output, :returns => [0, 100], :logoutput => :on_failure).refresh }.should 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) proc { resource.refresh }.should 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) catalog.add_resource tmp execer = Puppet::Type.type(:exec).new(:name => foo) catalog.add_resource execer catalog.relationship_graph.dependencies(execer).should == [tmp] 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" do before :each do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end 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') }. should raise_error Puppet::Error, /Parameter user failed/ 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" do before :each do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) 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') }. should 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) }.should_not raise_error end it "should fail if the provider calls the command invalid" do expect { test(command, false) }. should raise_error Puppet::Error, /Parameter #{@param} failed: 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 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 }. should 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 }. should 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] }. should 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') lambda { sleep_exec.refresh }.should 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 }. should 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] }. should 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 }. should 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] }. should 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 }. should raise_error(Puppet::Error, /Invalid value #{value.inspect}\. Valid values are true, false/ ) 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 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).should be + end + + it "should accept an absolute command with no path" do + type.new(:command => abs).should be + end + + it "should accept an absolute command with a path" do + type.new(:command => abs, :path => path).should be + end + end end diff --git a/test/ral/type/exec.rb b/test/ral/type/exec.rb deleted file mode 100755 index e610c76e7..000000000 --- a/test/ral/type/exec.rb +++ /dev/null @@ -1,727 +0,0 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../lib/puppettest') - -require 'puppettest' - -class TestExec < Test::Unit::TestCase - include PuppetTest - def test_numvsstring - [0, "0"].each { |val| - command = nil - output = nil - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - - :command => "/bin/echo", - - :returns => val - ) - } - assert_events([:executed_command], command) - } - end - - def test_path_or_qualified - command = nil - output = nil - assert_raise(Puppet::Error) { - command = Puppet::Type.type(:exec).new( - :command => "echo" - ) - } - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "echo", - :path => "/usr/bin:/bin:/usr/sbin:/sbin" - ) - } - assert_nothing_raised { - command = Puppet::Type.type(:exec).new( - :command => "/bin/echo" - ) - } - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "/bin/echo", - :path => "/usr/bin:/bin:/usr/sbin:/sbin" - ) - } - end - - def test_nonzero_returns - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "mkdir /this/directory/does/not/exist", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :returns => 1 - ) - } - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "touch /etc", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :returns => 1 - ) - } - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "thiscommanddoesnotexist", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :returns => 127 - ) - } - end - - def test_cwdsettings - command = nil - dir = "/tmp" - wd = Dir.chdir(dir) { - Dir.getwd - } - assert_nothing_raised { - - command = Puppet::Type.type(:exec).new( - :command => "pwd", - :cwd => dir, - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :returns => 0 - ) - } - assert_events([:executed_command], command) - assert_equal(wd,command.output.chomp) - end - - def test_refreshonly_functional - file = nil - cmd = nil - tmpfile = tempfile - @@tmpfiles.push tmpfile - trans = nil - - file = Puppet::Type.type(:file).new( - :path => tmpfile, - :content => "yay" - ) - # Get the file in sync - assert_apply(file) - - # Now make an exec - maker = tempfile - assert_nothing_raised { - cmd = Puppet::Type.type(:exec).new( - :command => "touch #{maker}", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :subscribe => file, - :refreshonly => true - ) - } - - assert(cmd, "did not make exec") - - assert_nothing_raised do - assert(! cmd.check_all_attributes, "Check passed when refreshonly is set") - end - - assert_events([], file, cmd) - assert(! FileTest.exists?(maker), "made file without refreshing") - - # Now change our content, so we throw a refresh - file[:content] = "yayness" - assert_events([:content_changed], file, cmd) - assert(FileTest.exists?(maker), "file was not made in refresh") - end - - def test_refreshonly - cmd = true - assert_nothing_raised { - cmd = Puppet::Type.type(:exec).new( - :command => "pwd", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :refreshonly => true - ) - } - - # Checks should always fail when refreshonly is enabled - assert(!cmd.check_all_attributes, "Check passed with refreshonly true") - - # Now make sure it passes if we pass in "true" - assert(cmd.check_all_attributes(true), "Check failed with refreshonly true while refreshing") - - # Now set it to false - cmd[:refreshonly] = false - assert(cmd.check_all_attributes, "Check failed with refreshonly false") - end - - def test_creates - file = tempfile - exec = nil - assert(! FileTest.exists?(file), "File already exists") - assert_nothing_raised { - - exec = Puppet::Type.type(:exec).new( - :command => "touch #{file}", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - :creates => file - ) - } - - comp = mk_catalog("createstest", exec) - assert_events([:executed_command], comp, "creates") - assert_events([], comp, "creates") - end - - # Verify that we can download the file that we're going to execute. - def test_retrievethenmkexe - exe = tempfile - oexe = tempfile - sh = %x{which sh} - File.open(exe, "w") { |f| f.puts "#!#{sh}\necho yup" } - - file = Puppet::Type.type(:file).new( - :path => oexe, - :source => exe, - :mode => 0755 - ) - - exec = Puppet::Type.type(:exec).new( - :command => oexe, - :require => Puppet::Resource.new(:file, oexe) - ) - - comp = mk_catalog("Testing", file, exec) - - assert_events([:file_created, :executed_command], comp) - end - - # Verify that we auto-require any managed scripts. - def test_autorequire_files - exe = tempfile - oexe = tempfile - sh = %x{which sh} - File.open(exe, "w") { |f| f.puts "#!#{sh}\necho yup" } - - - file = Puppet::Type.type(:file).new( - :path => oexe, - :source => exe, - :mode => 755 - ) - - basedir = File.dirname(oexe) - - baseobj = Puppet::Type.type(:file).new( - :path => basedir, - :source => exe, - :mode => 755 - ) - - - ofile = Puppet::Type.type(:file).new( - :path => exe, - :mode => 755 - ) - - - exec = Puppet::Type.type(:exec).new( - :command => oexe, - :path => ENV["PATH"], - :cwd => basedir - ) - - cat = Puppet::Type.type(:exec).new( - :command => "cat #{exe} #{oexe}", - :path => ENV["PATH"] - ) - - catalog = mk_catalog(file, baseobj, ofile, exec, cat) - - rels = nil - assert_nothing_raised do - rels = exec.autorequire - end - - # Verify we get the script itself - assert(rels.detect { |r| r.source == file }, "Exec did not autorequire its command") - - # Verify we catch the cwd - assert(rels.detect { |r| r.source == baseobj }, "Exec did not autorequire its cwd") - - # Verify we don't require ourselves - assert(! rels.detect { |r| r.source == ofile }, "Exec incorrectly required mentioned file") - - # We not longer autorequire inline files - assert_nothing_raised do - rels = cat.autorequire - end - assert(! rels.detect { |r| r.source == ofile }, "Exec required second inline file") - assert(! rels.detect { |r| r.source == file }, "Exec required inline file") - end - - def test_ifonly - afile = tempfile - bfile = tempfile - - exec = nil - assert_nothing_raised { - - exec = Puppet::Type.type(:exec).new( - :command => "touch #{bfile}", - :onlyif => "test -f #{afile}", - :path => ENV['PATH'] - ) - } - - assert_events([], exec) - system("touch #{afile}") - assert_events([:executed_command], exec) - assert_events([:executed_command], exec) - system("rm #{afile}") - assert_events([], exec) - end - - def test_unless - afile = tempfile - bfile = tempfile - - exec = nil - assert_nothing_raised { - exec = Puppet::Type.type(:exec).new( - :command => "touch #{bfile}", - :unless => "test -f #{afile}", - :path => ENV['PATH'] - ) - } - comp = mk_catalog(exec) - - assert_events([:executed_command], comp) - assert_events([:executed_command], comp) - system("touch #{afile}") - assert_events([], comp) - assert_events([], comp) - system("rm #{afile}") - assert_events([:executed_command], comp) - assert_events([:executed_command], comp) - end - - if Puppet.features.root? - # Verify that we can execute commands as a special user - def mknverify(file, user, group = nil, id = true) - File.umask(0022) - - args = { - :command => "touch #{file}", - :path => "/usr/bin:/bin:/usr/sbin:/sbin", - } - - if user - #Puppet.warning "Using user #{user.name}" - if id - # convert to a string, because that's what the object expects - args[:user] = user.uid.to_s - else - args[:user] = user.name - end - end - - if group - #Puppet.warning "Using group #{group.name}" - if id - args[:group] = group.gid.to_s - else - args[:group] = group.name - end - end - exec = nil - assert_nothing_raised { - exec = Puppet::Type.type(:exec).new(args) - } - - comp = mk_catalog("usertest", exec) - assert_events([:executed_command], comp, "usertest") - - assert(FileTest.exists?(file), "File does not exist") - assert_equal(user.uid, File.stat(file).uid, "File UIDs do not match") if user - - # We can't actually test group ownership, unfortunately, because - # behaviour changes wildlly based on platform. - Puppet::Type.allclear - end - - def test_userngroup - file = tempfile - [ - [nonrootuser], # just user, by name - [nonrootuser, nil, true], # user, by uid - [nil, nonrootgroup], # just group - [nil, nonrootgroup, true], # just group, by id - [nonrootuser, nonrootgroup], # user and group, by name - [nonrootuser, nonrootgroup, true], # user and group, by id - ].each { |ary| - mknverify(file, *ary) { - } - } - end - end - - def test_logoutput - exec = nil - assert_nothing_raised { - exec = Puppet::Type.type(:exec).new( - :title => "logoutputesting", - :path => "/usr/bin:/bin", - :command => "echo logoutput is false" - ) - } - - assert_equal(:on_failure, exec[:logoutput]) - assert_apply(exec) - - assert_nothing_raised { - exec[:command] = "echo logoutput is true" - exec[:logoutput] = true - } - - assert_equal(:true, exec[:logoutput]) - assert_apply(exec) - - assert_nothing_raised { - exec[:command] = "echo logoutput is false" - exec[:logoutput] = false - } - - assert_equal(:false, exec[:logoutput]) - assert_apply(exec) - - assert_nothing_raised { - exec[:command] = "echo logoutput is on_failure" - exec[:logoutput] = "on_failure" - } - - assert_equal(:on_failure, exec[:logoutput]) - assert_apply(exec) - end - - def test_execthenfile - exec = nil - file = nil - basedir = tempfile - path = File.join(basedir, "subfile") - assert_nothing_raised { - - exec = Puppet::Type.type(:exec).new( - :title => "mkdir", - :path => "/usr/bin:/bin", - :creates => basedir, - :command => "mkdir #{basedir}; touch #{path}" - ) - } - - assert_nothing_raised { - - file = Puppet::Type.type(:file).new( - :path => basedir, - :recurse => true, - :mode => "755", - :require => Puppet::Resource.new("exec", "mkdir") - ) - } - - comp = mk_catalog(file, exec) - comp.finalize - assert_events([:executed_command, :mode_changed], comp) - - assert(FileTest.exists?(path), "Exec ran first") - assert(File.stat(path).mode & 007777 == 0755) - end - - # Make sure all checks need to be fully qualified. - def test_falsevals - exec = nil - assert_nothing_raised do - exec = Puppet::Type.type(:exec).new( - :command => "/bin/touch yayness" - ) - end - - Puppet::Type.type(:exec).checks.each do |check| - klass = Puppet::Type.type(:exec).paramclass(check) - next if klass.value_collection.values.include? :false - assert_raise(Puppet::Error, "Check '#{check}' did not fail on false") do - exec[check] = false - end - end - end - - def test_createcwdandexe - exec1 = exec2 = nil - dir = tempfile - file = tempfile - - assert_nothing_raised { - exec1 = Puppet::Type.type(:exec).new( - :title => "one", - :path => ENV["PATH"], - :command => "mkdir #{dir}" - ) - } - - assert_nothing_raised("Could not create exec w/out existing cwd") { - - exec2 = Puppet::Type.type(:exec).new( - :title => "two", - :path => ENV["PATH"], - :command => "touch #{file}", - :cwd => dir - ) - } - - # Throw a check in there with our cwd and make sure it works - assert_nothing_raised("Could not check with a missing cwd") do - exec2[:unless] = "test -f /this/file/does/not/exist" - exec2.retrieve - end - - assert_raise(Puppet::Error) do - exec2.property(:returns).sync - end - - assert_nothing_raised do - exec2[:require] = exec1 - end - - assert_apply(exec1, exec2) - - assert(FileTest.exists?(file)) - end - - def test_checkarrays - exec = nil - file = tempfile - - test = "test -f #{file}" - - assert_nothing_raised { - exec = Puppet::Type.type(:exec).new( - :path => ENV["PATH"], - :command => "touch #{file}" - ) - } - - assert_nothing_raised { - exec[:unless] = test - } - - assert_nothing_raised { - assert(exec.check_all_attributes, "Check did not pass") - } - - assert_nothing_raised { - exec[:unless] = [test, test] - } - - - assert_nothing_raised { - exec.finish - } - - assert_nothing_raised { - assert(exec.check_all_attributes, "Check did not pass") - } - - assert_apply(exec) - - assert_nothing_raised { - assert(! exec.check_all_attributes, "Check passed") - } - end - - def test_missing_checks_cause_failures - # Solaris's sh exits with 1 here instead of 127 - return if Facter.value(:operatingsystem) == "Solaris" - - exec = Puppet::Type.type(:exec).new( - :command => "echo true", - :path => ENV["PATH"], - :onlyif => "/bin/nosuchthingexists" - ) - - assert_raise(ArgumentError, "Missing command did not raise error") { - exec.provider.run("/bin/nosuchthingexists") - } - end - - def test_environmentparam - - exec = Puppet::Type.newexec( - :command => "echo $environmenttest", - :path => ENV["PATH"], - :environment => "environmenttest=yayness" - ) - - assert(exec, "Could not make exec") - - output = status = nil - assert_nothing_raised { - output, status = exec.provider.run("echo $environmenttest") - } - - assert_equal("yayness\n", output) - - # Now check whether we can do multiline settings - assert_nothing_raised do - exec[:environment] = "environmenttest=a list of things -and stuff" - end - - output = status = nil - assert_nothing_raised { - output, status = exec.provider.run('echo "$environmenttest"') - } - assert_equal("a list of things\nand stuff\n", output) - - # Now test arrays - assert_nothing_raised do - exec[:environment] = ["funtest=A", "yaytest=B"] - end - - output = status = nil - assert_nothing_raised { - output, status = exec.provider.run('echo "$funtest" "$yaytest"') - } - assert_equal("A B\n", output) - end - - # Testing #470 - def test_run_as_created_user - exec = nil - if Process.uid == 0 - user = "nosuchuser" - assert_nothing_raised("Could not create exec with non-existent user") do - exec = Puppet::Type.type(:exec).new( - :command => "/bin/echo yay", - :user => user - ) - end - end - - # Now try the group - group = "nosuchgroup" - assert_nothing_raised("Could not create exec with non-existent user") do - - exec = Puppet::Type.type(:exec).new( - :command => "/bin/echo yay", - :group => group - ) - end - end - - # make sure paths work both as arrays and strings - def test_paths_as_arrays - path = %w{/usr/bin /usr/sbin /sbin} - exec = nil - assert_nothing_raised("Could not use an array for the path") do - exec = Puppet::Type.type(:exec).new(:command => "echo yay", :path => path) - end - assert_equal(path, exec[:path], "array-based path did not match") - assert_nothing_raised("Could not use a string for the path") do - exec = Puppet::Type.type(:exec).new(:command => "echo yay", :path => path.join(":")) - end - assert_equal(path, exec[:path], "string-based path did not match") - assert_nothing_raised("Could not use a colon-separated strings in an array for the path") do - exec = Puppet::Type.type(:exec).new(:command => "echo yay", :path => ["/usr/bin", "/usr/sbin:/sbin"]) - end - assert_equal(path, exec[:path], "colon-separated array path did not match") - end - - def test_checks_apply_to_refresh - file = tempfile - maker = tempfile - - exec = Puppet::Type.type(:exec).new( - :title => "maker", - :command => "touch #{maker}", - :path => ENV["PATH"] - ) - - # Make sure it runs normally - assert_apply(exec) - assert(FileTest.exists?(maker), "exec did not run") - File.unlink(maker) - - # Now make sure it refreshes - assert_nothing_raised("Failed to refresh exec") do - exec.refresh - end - assert(FileTest.exists?(maker), "exec did not run refresh") - File.unlink(maker) - - # Now add the checks - exec[:creates] = file - - # Make sure it runs when the file doesn't exist - assert_nothing_raised("Failed to refresh exec") do - exec.refresh - end - assert(FileTest.exists?(maker), "exec did not refresh when checks passed") - File.unlink(maker) - - # Now create the file and make sure it doesn't refresh - File.open(file, "w") { |f| f.puts "" } - assert_nothing_raised("Failed to refresh exec") do - exec.refresh - end - assert(! FileTest.exists?(maker), "exec refreshed with failing checks") - end - - def test_explicit_refresh - refresher = tempfile - maker = tempfile - - exec = Puppet::Type.type(:exec).new( - :title => "maker", - :command => "touch #{maker}", - :path => ENV["PATH"] - ) - - # Call refresh normally - assert_nothing_raised do - exec.refresh - end - - # Make sure it created the normal file - assert(FileTest.exists?(maker), "normal refresh did not work") - File.unlink(maker) - - # Now reset refresh, and make sure it wins - assert_nothing_raised("Could not set refresh parameter") do - exec[:refresh] = "touch #{refresher}" - end - assert_nothing_raised do - exec.refresh - end - - # Make sure it created the normal file - assert(FileTest.exists?(refresher), "refresh param was ignored") - assert(! FileTest.exists?(maker), "refresh param also ran command") - end - - if Puppet.features.root? - def test_autorequire_user - user = Puppet::Type.type(:user).new(:name => "yay") - exec = Puppet::Type.type(:exec).new(:command => "/bin/echo fun", :user => "yay") - - rels = nil - assert_nothing_raised("Could not evaluate autorequire") do - rels = exec.autorequire - end - assert(rels.find { |r| r.source == user and r.target == exec }, "Exec did not autorequire user") - end - end -end