diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb index cf7e997d8..0c28cf0df 100644 --- a/lib/puppet/parser/resource/reference.rb +++ b/lib/puppet/parser/resource/reference.rb @@ -1,93 +1,93 @@ # A reference to a resource. Mostly just the type and title. -require 'puppet/resource_reference' +require 'puppet/resource/reference' require 'puppet/file_collection/lookup' # A reference to a resource. Mostly just the type and title. class Puppet::Parser::Resource::Reference < Puppet::Resource::Reference include Puppet::FileCollection::Lookup include Puppet::Util::MethodHelper include Puppet::Util::Errors attr_accessor :builtin, :file, :line, :scope # Are we a builtin type? def builtin? unless defined? @builtin if builtintype() @builtin = true else @builtin = false end end @builtin end def builtintype if t = Puppet::Type.type(self.type.downcase) and t.name != :component t else nil end end # Return the defined type for our obj. This can return classes, # definitions or nodes. def definedtype unless defined? @definedtype case self.type when "Class" # look for host classes if self.title == :main tmp = @scope.findclass("") else unless tmp = @scope.parser.classes[self.title] fail Puppet::ParseError, "Could not find class '%s'" % self.title end end when "Node" # look for node definitions unless tmp = @scope.parser.nodes[self.title] fail Puppet::ParseError, "Could not find node '%s'" % self.title end else # normal definitions # The resource type is capitalized, so we have to downcase. Really, # we should have a better interface for finding these, but eh. tmp = @scope.parser.definitions[self.type.downcase] end if tmp @definedtype = tmp else fail Puppet::ParseError, "Could not find resource type '%s'" % self.type end end @definedtype end def initialize(hash) set_options(hash) requiredopts(:type, :title) end def to_ref # We have to return different cases to provide backward compatibility # from 0.24.x to 0.23.x. if builtin? return [type.to_s.downcase, title.to_s] else return [type.to_s, title.to_s] end end def typeclass unless defined? @typeclass if tmp = builtintype || definedtype @typeclass = tmp else fail Puppet::ParseError, "Could not find type %s" % self.type end end @typeclass end end diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index cd3b2ac1a..f13600680 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -1,213 +1,213 @@ # Provides utility functions to help interfaces Puppet to SELinux. # # This requires the very new SELinux Ruby bindings. These bindings closely # mirror the SELinux C library interface. # # Support for the command line tools is not provided because the performance # was abysmal. At this time (2008-11-02) the only distribution providing # these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby). begin require 'selinux' rescue LoadError # Nothing end module Puppet::Util::SELinux def selinux_support? unless defined? Selinux return false end if Selinux.is_selinux_enabled == 1 return true end return false end # Retrieve and return the full context of the file. If we don't have # SELinux support or if the SELinux call fails then return nil. def get_selinux_current_context(file) unless selinux_support? return nil end retval = Selinux.lgetfilecon(file) if retval == -1 return nil end return retval[1] end # Retrieve and return the default context of the file. If we don't have # SELinux support or if the SELinux call fails to file a default then return nil. def get_selinux_default_context(file) unless selinux_support? return nil end # If the filesystem has no support for SELinux labels, return a default of nil # instead of what matchpathcon would return unless selinux_label_support?(file) return nil end # If the file exists we should pass the mode to matchpathcon for the most specific # matching. If not, we can pass a mode of 0. begin filestat = File.lstat(file) mode = filestat.mode rescue Errno::ENOENT mode = 0 end retval = Selinux.matchpathcon(file, mode) if retval == -1 return nil end return retval[1] end # Take the full SELinux context returned from the tools and parse it # out to the three (or four) component parts. Supports :seluser, :selrole, # :seltype, and on systems with range support, :selrange. def parse_selinux_context(component, context) if context.nil? or context == "unlabeled" return nil end unless context =~ /^([a-z0-9_]+):([a-z0-9_]+):([a-z0-9_]+)(?::([a-zA-Z0-9:,._-]+))?/ raise Puppet::Error, "Invalid context to parse: #{context}" end ret = { :seluser => $1, :selrole => $2, :seltype => $3, :selrange => $4, } return ret[component] end # This updates the actual SELinux label on the file. You can update # only a single component or update the entire context. # The caveat is that since setting a partial context makes no sense the # file has to already exist. Puppet (via the File resource) will always # just try to set components, even if all values are specified by the manifest. # I believe that the OS should always provide at least a fall-through context # though on any well-running system. def set_selinux_context(file, value, component = false) unless selinux_support? return nil end if component # Must first get existing context to replace a single component context = Selinux.lgetfilecon(file)[1] if context == -1 # We can't set partial context components when no context exists # unless/until we can find a way to make Puppet call this method # once for all selinux file label attributes. Puppet.warning "Can't set SELinux context on file unless the file already has some kind of context" return nil end context = context.split(':') case component when :seluser context[0] = value when :selrole context[1] = value when :seltype context[2] = value when :selrange context[3] = value else raise ArguementError, "set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange" end context = context.join(':') else context = value end retval = Selinux.lsetfilecon(file, context) if retval == 0 return true else Puppet.warning "Failed to set SELinux context %s on %s" % [context, file] return false end end # Since this call relies on get_selinux_default_context it also needs a # full non-relative path to the file. Fortunately, that seems to be all # Puppet uses. This will set the file's SELinux context to the policy's # default context (if any) if it differs from the context currently on # the file. def set_selinux_default_context(file) new_context = get_selinux_default_context(file) unless new_context return nil end cur_context = get_selinux_current_context(file) if new_context != cur_context set_selinux_context(file, new_context) return new_context end return nil end # Internal helper function to read and parse /proc/mounts def read_mounts begin - mountfh = File.open("/proc/mounts", NONBLOCK) + mountfh = File.open("/proc/mounts", File::NONBLOCK) mounts = mountfh.read mountfh.close rescue return nil end mntpoint = {} # Read all entries in /proc/mounts. The second column is the # mountpoint and the third column is the filesystem type. # We skip rootfs because it is always mounted at / mounts.collect do |line| params = line.split(' ') next if params[2] == 'rootfs' mntpoint[params[1]] = params[2] end return mntpoint end # Internal helper function to return which type of filesystem a # given file path resides on def find_fs(file) unless mnts = read_mounts() return nil end # For a given file: # Check if the filename is in the data structure; # return the fstype if it is. # Just in case: return something if you're down to "/" or "" # Remove the last slash and everything after it, # and repeat with that as the file for the next loop through. ary = file.split('/') while not ary.empty? do path = ary.join('/') if mnts.has_key?(path) return mnts[path] end ary.pop end return mnts['/'] end # Check filesystem a path resides on for SELinux support against # whitelist of known-good filesystems. # Returns true if the filesystem can support SELinux labels and # false if not. def selinux_label_support?(file) fstype = find_fs(file) if fstype.nil? return false end filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs'] return filesystems.include?(fstype) end end diff --git a/spec/integration/bin/puppetmasterd.rb b/spec/integration/bin/puppetmasterd.rb index 447344472..b5a3f96da 100755 --- a/spec/integration/bin/puppetmasterd.rb +++ b/spec/integration/bin/puppetmasterd.rb @@ -1,109 +1,110 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe "puppetmasterd" do before do # Get a safe temporary file file = Tempfile.new("puppetmaster_integration_testing") @dir = file.path file.delete Dir.mkdir(@dir) Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet[:certdnsnames] = "localhost" @@port = 12345 end after { stop Puppet::SSL::Host.ca_location = :none system("rm -rf %s" % @dir) Puppet.settings.clear } def arguments rundir = File.join(Puppet[:vardir], "run") @pidfile = File.join(rundir, "testing.pid") args = "" args += " --confdir %s" % Puppet[:confdir] args += " --rundir %s" % rundir args += " --pidfile %s" % @pidfile args += " --vardir %s" % Puppet[:vardir] args += " --certdnsnames %s" % Puppet[:certdnsnames] args += " --masterport %s" % @@port args += " --user %s" % Puppet::Util::SUIDManager.uid args += " --group %s" % Puppet::Util::SUIDManager.gid args += " --autosign true" end def start(addl_args = "") Puppet.settings.mkdir(:manifestdir) Puppet.settings.write(:manifest) do |f| f.puts { "notify { testing: }" } end args = arguments + addl_args - output = %x{puppetmasterd #{args}}.chomp + bin = File.join(File.dirname(__FILE__), "..", "..", "..", "sbin", "puppetmasterd") + output = %x{#{bin} #{args}}.chomp end def stop if @pidfile and FileTest.exist?(@pidfile) pid = File.read(@pidfile).chomp.to_i Process.kill(:TERM, pid) end end it "should create a PID file" do start FileTest.exist?(@pidfile).should be_true end it "should be serving status information over REST" it "should be serving status information over xmlrpc" do start sleep 0.5 client = Puppet::Network::Client.status.new(:Server => "localhost", :Port => @@port) FileUtils.mkdir_p(File.dirname(Puppet[:autosign])) File.open(Puppet[:autosign], "w") { |f| f.puts Puppet[:certname] } client.cert retval = client.status retval.should == 1 end it "should exit with return code 0 after parsing if --parseonly is set and there are no errors" do start(" --parseonly > /dev/null") sleep(1) ps = Facter["ps"].value || "ps -ef" pid = nil %x{#{ps}}.chomp.split(/\n/).each { |line| next if line =~ /^puppet/ # skip normal master procs if line =~ /puppetmasterd.+--manifest/ ary = line.split(" ") pid = ary[1].to_i end } $?.should == 0 pid.should be_nil end it "should exit with return code 1 after parsing if --parseonly is set and there are errors" end diff --git a/spec/unit/provider/augeas/augeas.rb b/spec/unit/provider/augeas/augeas.rb index 284145657..43fca723c 100644 --- a/spec/unit/provider/augeas/augeas.rb +++ b/spec/unit/provider/augeas/augeas.rb @@ -1,365 +1,366 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' provider_class = Puppet::Type.type(:augeas).provider(:augeas) describe provider_class do describe "command parsing" do it "should break apart a single line into three tokens" do provider = provider_class.new() tokens = provider.parse_commands("set /Jar/Jar Binks") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" end it "should break apart a multiple line into six tokens" do provider = provider_class.new() tokens = provider.parse_commands("set /Jar/Jar Binks\nrm anakin skywalker") tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" tokens[1][2].should == "skywalker" end it "should handle arrays" do provider = provider_class.new() commands = ["set /Jar/Jar Binks", "rm anakin skywalker"] tokens = provider.parse_commands(commands) tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" tokens[1][2].should == "skywalker" end it "should concat the last values" do provider = provider_class.new() tokens = provider.parse_commands("set /Jar/Jar Binks is my copilot") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks is my copilot" end it "should accept spaces and and single ticks" do provider = provider_class.new() tokens = provider.parse_commands("set 'Jar Jar' Binks") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "Jar Jar" tokens[0][2].should == "Binks" end it "should accept spaces in the value and and single ticks" do provider = provider_class.new() tokens = provider.parse_commands("set 'Jar Jar' 'Binks is my copilot'") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "Jar Jar" tokens[0][2].should == "Binks is my copilot" end it "should accept spaces and and double ticks" do provider = provider_class.new() tokens = provider.parse_commands('set "Jar Jar" Binks') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == 'Jar Jar' tokens[0][2].should == 'Binks' end it "should accept spaces in the value and and double ticks" do provider = provider_class.new() tokens = provider.parse_commands('set "Jar Jar" "Binks is my copilot"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == 'Jar Jar' tokens[0][2].should == 'Binks is my copilot' end it "should accept mixed ticks" do provider = provider_class.new() tokens = provider.parse_commands('set "Jar Jar" "Some \'Test\'"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == 'Jar Jar' tokens[0][2].should == "Some \'Test\'" end it "should accept only the last value using ticks" do provider = provider_class.new() tokens = provider.parse_commands('set /Jar/Jar "Binks is my copilot"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == '/Jar/Jar' tokens[0][2].should == "Binks is my copilot" end it "should accept only the first value using ticks" do provider = provider_class.new() tokens = provider.parse_commands('set "Jar Jar" copilot') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == 'Jar Jar' tokens[0][2].should == "copilot" end it "should accept only the first value using ticks and the last values being concatenated" do provider = provider_class.new() tokens = provider.parse_commands('set "Jar Jar" Binks is my copilot') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == 'Jar Jar' tokens[0][2].should == "Binks is my copilot" end end describe "get filters" do before do augeas_stub = stub("augeas", :get => "value") @provider = provider_class.new() @provider.aug= augeas_stub end it "should return false for a = nonmatch" do command = ["get", "fake value", "==", "value"] @provider.process_get(command).should == true end it "should return true for a != match" do command = ["get", "fake value", "!=", "value"] @provider.process_get(command).should == false end it "should return true for a =~ match" do command = ["get", "fake value", "=~", "val*"] @provider.process_get(command).should == true end it "should return false for a == nonmatch" do command = ["get", "fake value", "=~", "num*"] @provider.process_get(command).should == false end end describe "match filters" do before do augeas_stub = stub("augeas", :match => ["set", "of", "values"]) @provider = provider_class.new() @provider.aug= augeas_stub end it "should return true for size match" do command = ["match", "fake value", "size", "==", "3"] @provider.process_match(command).should == true end it "should return false for a size non match" do command = ["match", "fake value", "size", "<", "3"] @provider.process_match(command).should == false end it "should return true for includes match" do command = ["get", "fake value", "include", "values"] @provider.process_match(command).should == true end it "should return false for includes non match" do command = ["get", "fake value", "include", "JarJar"] @provider.process_match(command).should == false end it "should return true for an array match" do command = ["get", "fake value", "==", "['set', 'of', 'values']"] @provider.process_match(command).should == true end it "should return false for an array non match" do command = ["get", "fake value", "==", "['this', 'should', 'not', 'match']"] @provider.process_match(command).should == false end end describe "need to run" do it "should handle no filters" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("") augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider = provider_class.new(resource) + provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return true when a get filter matches" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("get path == value") provider = provider_class.new(resource) augeas_stub = stub("augeas", :get => "value") augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return false when a get filter does not match" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("get path == another value") provider = provider_class.new(resource) augeas_stub = stub("augeas", :get => "value") augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end it "should return true when a match filter matches" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size == 3") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return false when a match filter does not match" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size == 2") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end #This is a copy of the last one, with setting the force to true it "setting force should not change the above logic" do resource = stub("resource") resource.stubs(:[]).returns(true).then.returns("match path size == 2") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end end describe "augeas execution integration" do before do @resource = stub("resource") @provider = provider_class.new(@resource) @augeas = stub("augeas") @provider.aug= @augeas @provider.stubs(:get_augeas_version).returns("0.3.5") end it "should handle set commands" do command = [["set", "/Jar/Jar", "Binks"]] context = "/some/path" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:set).with("/some/path/Jar/Jar", "Binks") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle rm commands" do command = [["rm", "/Jar/Jar"]] context = "" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle remove commands" do command = [["remove", "Jar/Jar"]] context = "" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle clear commands" do command = [["clear", "/Jar/Jar"]] context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:clear).with("/foo/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with before" do command = [["ins", "Binks", "before /Jar/Jar"]] context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with before" do command = [["ins", "Binks", "after /Jar/Jar"]] context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins with no context" do command = [["ins", "Binks", "after /Jar/Jar"]] context = "" # this is the default @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle multiple commands" do command = [["ins", "Binks", "after /Jar/Jar"], ["clear", "/Jar/Jar"]] context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", false) @augeas.expects(:clear).with("/foo/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end end end diff --git a/spec/unit/transaction.rb b/spec/unit/transaction.rb index efd071231..60705c7fb 100755 --- a/spec/unit/transaction.rb +++ b/spec/unit/transaction.rb @@ -1,48 +1,48 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/transaction' describe Puppet::Transaction do it "should match resources by name, not title, when prefetching" do - @catalog = Puppet::Node::Catalog.new + @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) # Have both a title and name resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" @catalog.add_resource resource resource.provider.class.expects(:prefetch).with("bar" => resource) @transaction.prefetch end end describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end it "should default to the tags specified in the :tags setting" do Puppet.expects(:[]).with(:tags).returns("one") @transaction.tags.should == %w{one} end it "should split tags based on ','" do Puppet.expects(:[]).with(:tags).returns("one,two") @transaction.tags.should == %w{one two} end it "should use any tags set after creation" do Puppet.expects(:[]).with(:tags).never @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end it "should always convert assigned tags to an array" do @transaction.tags = "one::two" @transaction.tags.should == %w{one::two} end end diff --git a/spec/unit/type/tidy.rb b/spec/unit/type/tidy.rb index e17f65de4..cf244ca0e 100755 --- a/spec/unit/type/tidy.rb +++ b/spec/unit/type/tidy.rb @@ -1,387 +1,380 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' tidy = Puppet::Type.type(:tidy) describe tidy do before do Puppet.settings.stubs(:use) end it "should use :lstat when stating a file" do - tidy = Puppet::Type.type(:tidy).new :path => "/foo/bar", :age => "1d" + resource = tidy.new :path => "/foo/bar", :age => "1d" stat = mock 'stat' File.expects(:lstat).with("/foo/bar").returns stat - tidy.stat("/foo/bar").should == stat + resource.stat("/foo/bar").should == stat end - it "should be in sync if the targeted file does not exist" do - File.expects(:lstat).with("/tmp/foonesslaters").raises Errno::ENOENT - @tidy = tidy.create :path => "/tmp/foonesslaters", :age => "100d" - - @tidy.property(:ensure).must be_insync({}) - end - [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| it "should have a %s parameter" % param do Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its %s param" % param do Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) end end describe "when validating parameter values" do describe "for 'recurse'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should allow 'true'" do lambda { @tidy[:recurse] = true }.should_not raise_error end it "should allow 'false'" do lambda { @tidy[:recurse] = false }.should_not raise_error end it "should allow integers" do lambda { @tidy[:recurse] = 10 }.should_not raise_error end it "should allow string representations of integers" do lambda { @tidy[:recurse] = "10" }.should_not raise_error end it "should allow 'inf'" do lambda { @tidy[:recurse] = "inf" }.should_not raise_error end it "should not allow arbitrary values" do lambda { @tidy[:recurse] = "whatever" }.should raise_error end end end describe "when matching files by age" do convertors = { :second => 1, :minute => 60 } convertors[:hour] = convertors[:minute] * 60 convertors[:day] = convertors[:hour] * 24 convertors[:week] = convertors[:day] * 7 convertors.each do |unit, multiple| it "should consider a %s to be %s seconds" % [unit, multiple] do tidy = Puppet::Type.type(:tidy).new :path => "/what/ever", :age => "5%s" % unit.to_s[0..0] tidy[:age].should == 5 * multiple end end end describe "when matching files by size" do convertors = { :b => 0, :kb => 1, :mb => 2, :gb => 3 } convertors.each do |unit, multiple| it "should consider a %s to be 1024^%s bytes" % [unit, multiple] do tidy = Puppet::Type.type(:tidy).new :path => "/what/ever", :size => "5%s" % unit total = 5 multiple.times { total *= 1024 } tidy[:size].should == total end end end describe "when tidying" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/what/ever" @stat = stub 'stat', :ftype => "directory" File.stubs(:lstat).with("/what/ever").returns @stat end describe "and generating files" do it "should set the backup on the file if backup is set on the tidy instance" do @tidy[:backup] = "whatever" Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } @tidy.mkfile("/what/ever") end it "should set the file's path to the tidy's path" do Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == "/what/ever" } @tidy.mkfile("/what/ever") end it "should configure the file for deletion" do Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } @tidy.mkfile("/what/ever") end it "should force deletion on the file" do Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } @tidy.mkfile("/what/ever") end it "should do nothing if the targeted file does not exist" do File.expects(:lstat).with("/what/ever").raises Errno::ENOENT @tidy.generate.should == [] end end describe "and recursion is not used" do it "should generate a file resource if the file should be tidied" do @tidy.expects(:tidy?).with("/what/ever").returns true file = Puppet::Type.type(:file).new(:path => "/eh") @tidy.expects(:mkfile).with("/what/ever").returns file @tidy.generate.should == [file] end it "should do nothing if the file should not be tidied" do @tidy.expects(:tidy?).with("/what/ever").returns false @tidy.expects(:mkfile).never @tidy.generate.should == [] end end describe "and recursion is used" do before do @tidy[:recurse] = true Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") @fileset = Puppet::FileServing::Fileset.new("/what/ever") Puppet::FileServing::Fileset.stubs(:new).returns @fileset end it "should use a Fileset for recursion" do Puppet::FileServing::Fileset.expects(:new).with("/what/ever", :recurse => true).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do @fileset.expects(:files).returns %w{. one two} @tidy.expects(:tidy?).with("/what/ever").returns true @tidy.expects(:tidy?).with("/what/ever/one").returns true @tidy.expects(:tidy?).with("/what/ever/two").returns false file = Puppet::Type.type(:file).new(:path => "/eh") @tidy.expects(:mkfile).with("/what/ever").returns file @tidy.expects(:mkfile).with("/what/ever/one").returns file @tidy.generate end end describe "and determining whether a file matches provided glob patterns" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/what/ever" @tidy[:matches] = %w{*foo* *bar*} @stat = mock 'stat' @matcher = @tidy.parameter(:matches) end it "should always convert the globs to an array" do @matcher.value = "*foo*" @matcher.value.should == %w{*foo*} end it "should return true if any pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.must be_tidy("/file/yaybarness", @stat) end it "should return false if no pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.should_not be_tidy("/file/yayness", @stat) end end describe "and determining whether a file is too old" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/what/ever" @stat = stub 'stat' @tidy[:age] = "1s" @tidy[:type] = "mtime" @ager = @tidy.parameter(:age) end it "should use the age type specified" do @tidy[:type] = :ctime @stat.expects(:ctime).returns(Time.now) @ager.tidy?("/what/ever", @stat) end it "should return false if the file is more recent than the specified age" do @stat.expects(:mtime).returns(Time.now) @ager.should_not be_tidy("/what/ever", @stat) end it "should return true if the file is older than the specified age" do @stat.expects(:mtime).returns(Time.now - 10) @ager.must be_tidy("/what/ever", @stat) end end describe "and determining whether a file is too large" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/what/ever" @stat = stub 'stat', :ftype => "file" @tidy[:size] = "1kb" @sizer = @tidy.parameter(:size) end it "should return false if the file is smaller than the specified size" do @stat.expects(:size).returns(4) # smaller than a kilobyte @sizer.should_not be_tidy("/what/ever", @stat) end it "should return true if the file is larger than the specified size" do @stat.expects(:size).returns(1500) # larger than a kilobyte @sizer.must be_tidy("/what/ever", @stat) end end describe "and determining whether a file should be tidied" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/what/ever" @stat = stub 'stat', :ftype => "file" File.stubs(:lstat).with("/what/ever").returns @stat end it "should not try to recurse if the file does not exist" do @tidy[:recurse] = true File.stubs(:lstat).with("/what/ever").returns nil @tidy.generate.should == [] end it "should not be tidied if the file does not exist" do File.expects(:lstat).with("/what/ever").raises Errno::ENOENT @tidy.should_not be_tidy("/what/ever") end it "should not be tidied if the user has no access to the file" do File.expects(:lstat).with("/what/ever").raises Errno::EACCES @tidy.should_not be_tidy("/what/ever") end it "should not be tidied if it is a directory and rmdirs is set to false" do stat = mock 'stat', :ftype => "directory" File.expects(:lstat).with("/what/ever").returns stat @tidy.should_not be_tidy("/what/ever") end it "should return false if it does not match any provided globs" do @tidy[:matches] = "globs" matches = @tidy.parameter(:matches) matches.expects(:tidy?).with("/what/ever", @stat).returns false @tidy.should_not be_tidy("/what/ever") end it "should return false if it does not match aging requirements" do @tidy[:age] = "1d" ager = @tidy.parameter(:age) ager.expects(:tidy?).with("/what/ever", @stat).returns false @tidy.should_not be_tidy("/what/ever") end it "should return false if it does not match size requirements" do @tidy[:size] = "1b" sizer = @tidy.parameter(:size) sizer.expects(:tidy?).with("/what/ever", @stat).returns false @tidy.should_not be_tidy("/what/ever") end it "should tidy a file if age and size are set but only size matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns true @tidy.parameter(:age).stubs(:tidy?).returns false @tidy.should be_tidy("/what/ever") end it "should tidy a file if age and size are set but only age matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns false @tidy.parameter(:age).stubs(:tidy?).returns true @tidy.should be_tidy("/what/ever") end it "should tidy all files if neither age nor size is set" do @tidy.must be_tidy("/what/ever") end it "should sort the results inversely by path length, so files are added to the catalog before their directories" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = Puppet::FileServing::Fileset.new("/what/ever") Puppet::FileServing::Fileset.expects(:new).returns fileset fileset.expects(:files).returns %w{. one one/two} @tidy.stubs(:tidy?).returns true @tidy.generate.collect { |r| r[:path] }.should == %w{/what/ever/one/two /what/ever/one /what/ever} end end it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = mock 'fileset' Puppet::FileServing::Fileset.expects(:new).with("/what/ever", :recurse => true).returns fileset fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} @tidy.stubs(:tidy?).returns true result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } { "/what/ever" => %w{/what/ever/one /what/ever/two}, "/what/ever/one" => ["/what/ever/one/subone"], "/what/ever/two" => ["/what/ever/two/subtwo"], "/what/ever/one/subone" => ["/what/ever/one/subone/ssone"] }.each do |parent, children| children.each do |child| ref = Puppet::Resource::Reference.new(:file, child) result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil end end end end end diff --git a/spec/unit/util/selinux.rb b/spec/unit/util/selinux.rb index dacf9f503..db3ee413a 100644 --- a/spec/unit/util/selinux.rb +++ b/spec/unit/util/selinux.rb @@ -1,219 +1,222 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/util/selinux' include Puppet::Util::SELinux unless defined?(Selinux) module Selinux def self.is_selinux_enabled false end end end describe Puppet::Util::SELinux do describe "selinux_support?" do before do end it "should return :true if this system has SELinux enabled" do Selinux.expects(:is_selinux_enabled).returns 1 selinux_support?.should be_true end it "should return :false if this system lacks SELinux" do Selinux.expects(:is_selinux_enabled).returns 0 selinux_support?.should be_false end end describe "filesystem detection" do before :each do - File.expects(:read).with("/proc/mounts").returns "rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n" + fh = stub 'fh', :close => nil + File.stubs(:open).with("/proc/mounts", File::NONBLOCK).returns fh + fh.stubs(:read).returns "rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n" + fh.stubs(:close) end it "should parse the contents of /proc/mounts" do read_mounts().should == { '/' => 'ext3', '/sys' => 'sysfs', '/mnt/nfs' => 'nfs', '/proc' => 'proc', '/dev' => 'tmpfs' } end it "should match a path on / to ext3" do find_fs('/etc/puppet/testfile').should == "ext3" end it "should match a path on /mnt/nfs to nfs" do find_fs('/mnt/nfs/testfile/foobar').should == "nfs" end it "should reture true for a capable filesystem" do selinux_label_support?('/etc/puppet/testfile').should be_true end it "should return false for a noncapable filesystem" do selinux_label_support?('/mnt/nfs/testfile').should be_false end end describe "get_selinux_current_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_current_context("/foo").should be_nil end it "should return a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if lgetfilecon fails" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns -1 get_selinux_current_context("/foo").should be_nil end end describe "get_selinux_default_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_default_context("/foo").should be_nil end it "should return a context if a default context exists" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if matchpathcon returns failure" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 get_selinux_default_context("/foo").should be_nil end it "should return nil if selinux_label_support returns false" do self.expects(:selinux_support?).returns true self.expects(:find_fs).with("/foo").returns "nfs" get_selinux_default_context("/foo").should be_nil end end describe "parse_selinux_context" do it "should return nil if no context is passed" do parse_selinux_context(:seluser, nil).should be_nil end it "should return nil if the context is 'unlabeled'" do parse_selinux_context(:seluser, "unlabeled").should be_nil end it "should return the user type when called with :seluser" do parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" end it "should return the role type when called with :selrole" do parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" end it "should return the type type when called with :seltype" do parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" end it "should return nil for :selrange when no range is returned" do parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil end it "should return the range type when called with :selrange" do parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" end describe "with a variety of SELinux range formats" do ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| it "should parse range '#{range}'" do parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range end end end end describe "set_selinux_context" do it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil end it "should use lsetfilecon to set a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true end it "should use lsetfilecon to set user_u user context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u", :seluser).should be_true end it "should use lsetfilecon to set role_r role context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "role_r", :selrole).should be_true end it "should use lsetfilecon to set type_t type context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "type_t", :seltype).should be_true end it "should use lsetfilecon to set s0:c3,c5 range context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0 set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true end end describe "set_selinux_default_context" do it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_default_context("/foo").should be_nil end it "should return nil if no default context exists" do self.expects(:get_selinux_default_context).with("/foo").returns nil set_selinux_default_context("/foo").should be_nil end it "should do nothing and return nil if the current context matches the default context" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" set_selinux_default_context("/foo").should be_nil end it "should set and return the default context if current and default do not match" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true set_selinux_default_context("/foo").should == "user_u:role_r:type_t" end end end diff --git a/test/network/client/resource.rb b/test/network/client/resource.rb index ba84fc27e..c73690170 100755 --- a/test/network/client/resource.rb +++ b/test/network/client/resource.rb @@ -1,55 +1,52 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../lib/puppettest' require 'puppettest' require 'puppettest/support/utils' require 'puppettest/support/assertions' require 'puppet/network/client/resource' class TestResourceClient < Test::Unit::TestCase include PuppetTest::ServerTest include PuppetTest::Support::Utils def setup super Puppet::Type.type(:user).provider(:directoryservice).stubs(:get_macosx_version_major).returns "10.5" end def mkresourceserver Puppet::Network::Handler.resource.new end def mkclient client = Puppet::Network::Client.resource.new(:Resource => mkresourceserver) end def test_resources file = tempfile() text = "yayness\n" File.open(file, "w") { |f| f.print text } mkresourceserver() client = mkclient() # Test describing tresource = client.describe("file", file) assert(tresource, "Did not get response") assert_instance_of(Puppet::TransObject, tresource) resource = tresource.to_ral - assert_events([], resource) - File.unlink(file) - assert_events([:file_created], resource) - File.unlink(file) + assert_equal(File.stat(file).mode & 007777, resource[:mode], "Did not get mode") # Now test applying result = client.apply(tresource) assert(FileTest.exists?(file), "File was not created on apply") end end