diff --git a/lib/puppet/provider/selmodule/semodule.rb b/lib/puppet/provider/selmodule/semodule.rb index 50ba039ec..d7ce77fd6 100644 --- a/lib/puppet/provider/selmodule/semodule.rb +++ b/lib/puppet/provider/selmodule/semodule.rb @@ -1,135 +1,134 @@ Puppet::Type.type(:selmodule).provide(:semodule) do desc "Manage SELinux policy modules using the semodule binary." commands :semodule => "/usr/sbin/semodule" def create begin execoutput("#{command(:semodule)} --install #{selmod_name_to_filename}") rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not load policy module: #{detail}"; end :true end def destroy execoutput("#{command(:semodule)} --remove #{@resource[:name]}") rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not remove policy module: #{detail}"; end def exists? self.debug "Checking for module #{@resource[:name]}" execpipe("#{command(:semodule)} --list") do |out| out.each_line do |line| if line =~ /#{@resource[:name]}\b/ return :true end end end nil end def syncversion self.debug "Checking syncversion on #{@resource[:name]}" loadver = selmodversion_loaded if(loadver) then filever = selmodversion_file if (filever == loadver) return :true end end :false end def syncversion= (dosync) execoutput("#{command(:semodule)} --upgrade #{selmod_name_to_filename}") rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not upgrade policy module: #{detail}"; end # Helper functions def execoutput (cmd) output = '' begin execpipe(cmd) do |out| output = out.readlines.join('').chomp! end rescue Puppet::ExecutionFailure raise Puppet::ExecutionFailure, output.split("\n")[0] end output end def selmod_name_to_filename if @resource[:selmodulepath] return @resource[:selmodulepath] else return "#{@resource[:selmoduledir]}/#{@resource[:name]}.pp" end end def selmod_readnext (handle) len = handle.read(4).unpack('L')[0] handle.read(len) end def selmodversion_file magic = 0xF97CFF8F filename = selmod_name_to_filename mod = File.new(filename, "r") (hdr, ver, numsec) = mod.read(12).unpack('LLL') raise Puppet::Error, "Found #{hdr} instead of magic #{magic} in #{filename}" if hdr != magic raise Puppet::Error, "Unknown policy file version #{ver} in #{filename}" if ver != 1 # Read through (and throw away) the file section offsets, and also # the magic header for the first section. mod.read((numsec + 1) * 4) ## Section 1 should be "SE Linux Module" selmod_readnext(mod) selmod_readnext(mod) # Skip past the section headers mod.read(14) # Module name selmod_readnext(mod) # At last! the version v = selmod_readnext(mod) self.debug "file version #{v}" v end def selmodversion_loaded lines = () begin execpipe("#{command(:semodule)} --list") do |output| - lines = output.readlines - lines.each_line do |line| + output.each_line do |line| line.chomp! bits = line.split if bits[0] == @resource[:name] self.debug "load version #{bits[1]}" return bits[1] end end end rescue Puppet::ExecutionFailure raise Puppet::ExecutionFailure, "Could not list policy modules: #{lines.join(' ').chomp!}" end nil end end diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb index 76ec91a05..4f4c32c9f 100755 --- a/spec/unit/provider/package/dpkg_spec.rb +++ b/spec/unit/provider/package/dpkg_spec.rb @@ -1,226 +1,227 @@ #!/usr/bin/env rspec require 'spec_helper' +require 'stringio' provider = Puppet::Type.type(:package).provider(:dpkg) describe provider do before do @resource = stub 'resource', :[] => "asdf" @provider = provider.new(@resource) @provider.expects(:execute).never # forbid "manual" executions @fakeresult = "install ok installed asdf 1.0\n" end it "should have documentation" do provider.doc.should be_instance_of(String) end describe "when listing all instances" do before do provider.stubs(:command).with(:dpkgquery).returns "myquery" end it "should use dpkg-query" do provider.expects(:command).with(:dpkgquery).returns "myquery" - provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").returns @fakeresult + provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").yields StringIO.new(@fakeresult) provider.instances end it "should create and return an instance with each parsed line from dpkg-query" do pipe = mock 'pipe' pipe.expects(:each).never pipe.expects(:each_line).yields @fakeresult provider.expects(:execpipe).yields pipe asdf = mock 'pkg1' provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf provider.instances.should == [asdf] end it "should warn on and ignore any lines it does not understand" do pipe = mock 'pipe' pipe.expects(:each).never pipe.expects(:each_line).yields "foobar" provider.expects(:execpipe).yields pipe Puppet.expects(:warning) provider.expects(:new).never provider.instances.should == [] end end describe "when querying the current state" do it "should use dpkg-query" do @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult @provider.query end it "should consider the package purged if dpkg-query fails" do @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") @provider.query[:ensure].should == :purged end it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do @provider.expects(:dpkgquery).returns @fakeresult @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} end it "should consider the package absent if the dpkg-query result cannot be interpreted" do @provider.expects(:dpkgquery).returns "somebaddata" @provider.query[:ensure].should == :absent end it "should fail if an error is discovered" do @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") lambda { @provider.query }.should raise_error(Puppet::Error) end it "should consider the package purged if it is marked 'not-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") @provider.query[:ensure].should == :purged end it "should consider the package absent if it is marked 'config-files'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "config-files") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-installed") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'unpacked'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "unpacked") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-configured'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-configured") @provider.query[:ensure].should == :absent end it "should consider the package held if its state is 'hold'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("install", "hold") @provider.query[:ensure].should == :held end end it "should be able to install" do @provider.should respond_to(:install) end describe "when installing" do before do @resource.stubs(:[]).with(:source).returns "mypkg" end it "should fail to install if no source is specified in the resource" do @resource.expects(:[]).with(:source).returns nil lambda { @provider.install }.should raise_error(ArgumentError) end it "should use 'dpkg -i' to install the package" do @resource.expects(:[]).with(:source).returns "mypackagefile" @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } @provider.install end it "should keep old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :keep @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } @provider.install end it "should replace old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :replace @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } @provider.install end it "should ensure any hold is removed" do @provider.expects(:unhold).once @provider.expects(:dpkg) @provider.install end end describe "when holding or unholding" do before do @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" @tempfile.stubs(:write) Tempfile.stubs(:new).returns @tempfile end it "should install first if holding" do @provider.stubs(:execute) @provider.expects(:install).once @provider.hold end it "should execute dpkg --set-selections when holding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end it "should execute dpkg --set-selections when unholding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end end it "should use :install to update" do @provider.expects(:install) @provider.update end describe "when determining latest available version" do it "should return the version found by dpkg-deb" do @resource.expects(:[]).with(:source).returns "myfile" @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" @provider.latest.should == "1.0" end it "should warn if the package file contains a different package" do @provider.expects(:dpkg_deb).returns("foo\tversion") @provider.expects(:warning) @provider.latest end it "should cope with names containing ++" do @resource = stub 'resource', :[] => "asdf++" @provider = provider.new(@resource) @provider.expects(:dpkg_deb).returns "asdf++\t1.0" @provider.latest.should == "1.0" end end it "should use 'dpkg -r' to uninstall" do @provider.expects(:dpkg).with("-r", "asdf") @provider.uninstall end it "should use 'dpkg --purge' to purge" do @provider.expects(:dpkg).with("--purge", "asdf") @provider.purge end end diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb index 88be7906a..1289c6ea1 100755 --- a/spec/unit/provider/package/openbsd_spec.rb +++ b/spec/unit/provider/package/openbsd_spec.rb @@ -1,114 +1,115 @@ #!/usr/bin/env rspec require 'spec_helper' +require 'stringio' provider_class = Puppet::Type.type(:package).provider(:openbsd) describe provider_class do subject { provider_class } def package(args = {}) defaults = { :name => 'bash', :provider => 'openbsd' } Puppet::Type.type(:package).new(defaults.merge(args)) end before :each do # Stub some provider methods to avoid needing the actual software # installed, so we can test on whatever platform we want. provider_class.stubs(:command).with(:pkginfo).returns('/bin/pkg_info') provider_class.stubs(:command).with(:pkgadd).returns('/bin/pkg_add') provider_class.stubs(:command).with(:pkgdelete).returns('/bin/pkg_delete') end context "::instances" do it "should return nil if execution failed" do subject.expects(:execpipe).raises(Puppet::ExecutionFailure, 'wawawa') subject.instances.should be_nil end it "should return the empty set if no packages are listed" do - subject.expects(:execpipe).with(%w{/bin/pkg_info -a}).yields('') + subject.expects(:execpipe).with(%w{/bin/pkg_info -a}).yields(StringIO.new('')) subject.instances.should be_empty end it "should return all packages when invoked" do - fixture = File.read(my_fixture('pkginfo.list')) + fixture = File.new(my_fixture('pkginfo.list')) subject.expects(:execpipe).with(%w{/bin/pkg_info -a}).yields(fixture) subject.instances.map(&:name).sort.should == %w{bash bzip2 expat gettext libiconv lzo openvpn python vim wget}.sort end end context "#install" do it "should fail if the resource doesn't have a source" do provider = subject.new(package()) expect { provider.install }. to raise_error Puppet::Error, /must specify a package source/ end it "should install correctly when given a directory-unlike source" do ENV.should_not be_key 'PKG_PATH' source = '/whatever.pkg' provider = subject.new(package(:source => source)) provider.expects(:pkgadd).with do |name| ENV.should_not be_key 'PKG_PATH' name == source end provider.install ENV.should_not be_key 'PKG_PATH' end it "should install correctly when given a directory-like source" do ENV.should_not be_key 'PKG_PATH' source = '/whatever/' provider = subject.new(package(:source => source)) provider.expects(:pkgadd).with do |name| ENV.should be_key 'PKG_PATH' ENV['PKG_PATH'].should == source name == provider.resource[:name] end provider.install ENV.should_not be_key 'PKG_PATH' end end context "#get_version" do it "should return nil if execution fails" do provider = subject.new(package) provider.expects(:execpipe).raises(Puppet::ExecutionFailure, 'wawawa') provider.get_version.should be_nil end it "should return the package version if in the output" do - fixture = File.read(my_fixture('pkginfo.list')) + fixture = File.new(my_fixture('pkginfo.list')) provider = subject.new(package(:name => 'bash')) provider.expects(:execpipe).with(%w{/bin/pkg_info -I bash}).yields(fixture) provider.get_version.should == '3.1.17' end it "should return the empty string if the package is not present" do provider = subject.new(package(:name => 'zsh')) - provider.expects(:execpipe).with(%w{/bin/pkg_info -I zsh}).yields('') + provider.expects(:execpipe).with(%w{/bin/pkg_info -I zsh}).yields(StringIO.new('')) provider.get_version.should == '' end end context "#query" do it "should return the installed version if present" do fixture = File.read(my_fixture('pkginfo.detail')) provider = subject.new(package(:name => 'bash')) provider.expects(:pkginfo).with('bash').returns(fixture) provider.query.should == { :ensure => '3.1.17' } end it "should return nothing if not present" do provider = subject.new(package(:name => 'zsh')) provider.expects(:pkginfo).with('zsh').returns('') provider.query.should be_nil end end end diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb index 5d9af5329..fbc4babdf 100755 --- a/spec/unit/provider/package/pacman_spec.rb +++ b/spec/unit/provider/package/pacman_spec.rb @@ -1,237 +1,238 @@ #!/usr/bin/env rspec require 'spec_helper' +require 'stringio' provider = Puppet::Type.type(:package).provider(:pacman) describe provider do before do provider.stubs(:command).with(:pacman).returns('/usr/bin/pacman') @resource = stub 'resource' @resource.stubs(:[]).returns("package") @resource.stubs(:name).returns("name") @provider = provider.new(@resource) end describe "when installing" do before do @provider.stubs(:query).returns({ :ensure => '1.0' }) end it "should call pacman" do provider. expects(:execute). at_least_once. with { |args| args[0] == "/usr/bin/pacman" }. returns "" @provider.install end it "should be quiet" do provider. expects(:execute). with { |args| args[1,2] == ["--noconfirm", "--noprogressbar"] }. returns("") @provider.install end it "should install the right package" do provider. expects(:execute). with { |args| args[3,4] == ["-Sy", @resource[0]] }. returns("") @provider.install end it "should raise an ExecutionFailure if the installation failed" do provider.stubs(:execute).returns("") @provider.expects(:query).returns(nil) lambda { @provider.install }.should raise_exception(Puppet::ExecutionFailure) end end describe "when updating" do it "should call install" do @provider.expects(:install).returns("install return value") @provider.update.should == "install return value" end end describe "when uninstalling" do it "should call pacman" do provider. expects(:execute). with { |args| args[0] == "/usr/bin/pacman" }. returns "" @provider.uninstall end it "should be quiet" do provider. expects(:execute). with { |args| args[1,2] == ["--noconfirm", "--noprogressbar"] }. returns("") @provider.uninstall end it "should remove the right package" do provider. expects(:execute). with { |args| args[3,4] == ["-R", @resource[0]] }. returns("") @provider.uninstall end end describe "when querying" do it "should query pacman" do provider. expects(:execute). with(["/usr/bin/pacman", "-Qi", @resource[0]]) @provider.query end it "should return the version" do query_output = <=2.7.1 libfetch>=2.25 pacman-mirrorlist Optional Deps : fakeroot: for makepkg usage as normal user curl: for rankmirrors usage Required By : None Conflicts With : None Replaces : None Installed Size : 2352.00 K Packager : Dan McGee Architecture : i686 Build Date : Sat 22 Jan 2011 03:56:41 PM EST Install Date : Thu 27 Jan 2011 06:45:49 AM EST Install Reason : Explicitly installed Install Script : Yes Description : A library-based package manager with dependency support EOF provider.expects(:execute).returns(query_output) @provider.query.should == {:ensure => "1.01.3-2"} end it "should return a nil if the package isn't found" do provider.expects(:execute).returns("") @provider.query.should be_nil end it "should return a hash indicating that the package is missing on error" do provider.expects(:execute).raises(Puppet::ExecutionFailure.new("ERROR!")) @provider.query.should == { :ensure => :purged, :status => 'missing', :name => @resource[0], :error => 'ok', } end end describe "when fetching a package list" do it "should query pacman" do provider.expects(:execpipe).with(["/usr/bin/pacman", '-Q']) provider.instances end it "should return installed packages with their versions" do - provider.expects(:execpipe).yields("package1 1.23-4\npackage2 2.00\n") + provider.expects(:execpipe).yields(StringIO.new("package1 1.23-4\npackage2 2.00\n")) packages = provider.instances packages.length.should == 2 packages[0].properties.should == { :provider => :pacman, :ensure => '1.23-4', :name => 'package1' } packages[1].properties.should == { :provider => :pacman, :ensure => '2.00', :name => 'package2' } end it "should return nil on error" do provider.expects(:execpipe).raises(Puppet::ExecutionFailure.new("ERROR!")) provider.instances.should be_nil end it "should warn on invalid input" do - provider.expects(:execpipe).yields("blah") + provider.expects(:execpipe).yields(StringIO.new("blah")) provider.expects(:warning).with("Failed to match line blah") provider.instances.should == [] end end describe "when determining the latest version" do it "should refresh package list" do refreshed = states('refreshed').starts_as('unrefreshed') provider. expects(:execute). when(refreshed.is('unrefreshed')). with(['/usr/bin/pacman', '-Sy']). then(refreshed.is('refreshed')) provider. stubs(:execute). when(refreshed.is('refreshed')). returns("") @provider.latest end it "should get query pacman for the latest version" do refreshed = states('refreshed').starts_as('unrefreshed') provider. stubs(:execute). when(refreshed.is('unrefreshed')). then(refreshed.is('refreshed')) provider. expects(:execute). when(refreshed.is('refreshed')). with(['/usr/bin/pacman', '-Sp', '--print-format', '%v', @resource[0]]). returns("") @provider.latest end it "should return the version number from pacman" do provider. expects(:execute). at_least_once(). returns("1.00.2-3\n") @provider.latest.should == "1.00.2-3" end end end diff --git a/spec/unit/provider/selmodule_spec.rb b/spec/unit/provider/selmodule_spec.rb index 2ec6fc2db..199f2edf7 100755 --- a/spec/unit/provider/selmodule_spec.rb +++ b/spec/unit/provider/selmodule_spec.rb @@ -1,66 +1,73 @@ #!/usr/bin/env rspec # Note: This unit test depends on having a sample SELinux policy file # in the same directory as this test called selmodule-example.pp # with version 1.5.0. The provided selmodule-example.pp is the first # 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 require 'spec_helper' +require 'stringio' provider_class = Puppet::Type.type(:selmodule).provider(:semodule) describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @provider = provider_class.new(@resource) end describe "exists? method" do it "should find a module if it is already loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields "bar\t1.2.3\nfoo\t4.4.4\nbang\t1.0.0\n" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nfoo\t4.4.4\nbang\t1.0.0\n") @provider.exists?.should == :true end it "should return nil if not loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields "bar\t1.2.3\nbang\t1.0.0\n" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nbang\t1.0.0\n") @provider.exists?.should be_nil end it "should return nil if no modules are loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" - @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields "" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("") @provider.exists?.should be_nil end end describe "selmodversion_file" do it "should return 1.5.0 for the example policy file" do @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" @provider.selmodversion_file.should == "1.5.0" end end describe "syncversion" do it "should return :true if loaded and file modules are in sync" do @provider.expects(:selmodversion_loaded).returns "1.5.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :true end it "should return :false if loaded and file modules are not in sync" do @provider.expects(:selmodversion_loaded).returns "1.4.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :false end it "should return before checking file version if no loaded policy" do @provider.expects(:selmodversion_loaded).returns nil @provider.syncversion.should == :false end - end + describe "selmodversion_loaded" do + it "should return the version of a loaded module" do + @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" + @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nfoo\t4.4.4\nbang\t1.0.0\n") + @provider.selmodversion_loaded.should == "4.4.4" + end + end end