diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb index 9d813bd5a..4caa1420c 100644 --- a/lib/puppet/provider/service/launchd.rb +++ b/lib/puppet/provider/service/launchd.rb @@ -1,266 +1,296 @@ require 'facter/util/plist' - Puppet::Type.type(:service).provide :launchd, :parent => :base do desc "launchd service management framework. This provider manages jobs with launchd, which is the default service framework for Mac OS X and is potentially available for use on other platforms. See: - * http://developer.apple.com/macosx/launchd.html * http://launchd.macosforge.org/ This provider reads plists out of the following directories: - * /System/Library/LaunchDaemons * /System/Library/LaunchAgents * /Library/LaunchDaemons * /Library/LaunchAgents ...and builds up a list of services based upon each plist's \"Label\" entry. This provider supports: - * ensure => running/stopped, * enable => true/false * status * restart Here is how the Puppet states correspond to launchd states: - * stopped --- job unloaded * started --- job loaded * enabled --- 'Disable' removed from job plist file * disabled --- 'Disable' added to job plist file Note that this allows you to do something launchctl can't do, which is to be in a state of \"stopped/enabled\ or \"running/disabled\". " + include Puppet::Util::Warnings + commands :launchctl => "/bin/launchctl" - commands :sw_vers => "/usr/bin/sw_vers" - commands :plutil => "/usr/bin/plutil" + commands :sw_vers => "/usr/bin/sw_vers" + commands :plutil => "/usr/bin/plutil" defaultfor :operatingsystem => :darwin - confine :operatingsystem => :darwin + confine :operatingsystem => :darwin has_feature :enableable + mk_resource_methods - Launchd_Paths = ["/Library/LaunchAgents", - "/Library/LaunchDaemons", - "/System/Library/LaunchAgents", - "/System/Library/LaunchDaemons",] + Launchd_Paths = [ "/Library/LaunchAgents", + "/Library/LaunchDaemons", + "/System/Library/LaunchAgents", + "/System/Library/LaunchDaemons"] Launchd_Overrides = "/var/db/launchd.db/com.apple.launchd/overrides.plist" + + # Caching is enabled through the following three methods. Self.prefetch will + # call self.instances to create an instance for each service. Self.flush will + # clear out our cache when we're done. + def self.prefetch(resources) + instances.each do |prov| + if resource = resources[prov.name] + resource.provider = prov + end + end + end - - # Read a plist, whether its format is XML or in Apple's "binary1" - # format. - def self.read_plist(path) - Plist::parse_xml(plutil('-convert', 'xml1', '-o', '/dev/stdout', path)) + # Self.instances will return an array with each element being a hash + # containing the name, provider, path, and status of each service on the + # system. + def self.instances + jobs = self.jobsearch + @job_list ||= self.job_list + jobs.keys.collect do |job| + job_status = @job_list.has_key?(job) ? :running : :stopped + new(:name => job, :provider => :launchd, :path => jobs[job], :status => job_status) + end end - # returns a label => path map for either all jobs, or just a single - # job if the label is specified + # Sets a class instance variable with a hash of all launchd plist files that + # are found on the system. The key of the hash is the job id and the value + # is the path to the file. If a label is passed, we return the job id and + # path for that specific job. def self.jobsearch(label=nil) - label_to_path_map = {} - Launchd_Paths.each do |path| - if FileTest.exists?(path) - Dir.entries(path).each do |f| - next if f =~ /^\..*$/ - next if FileTest.directory?(f) - fullpath = File.join(path, f) - if FileTest.file?(fullpath) and job = read_plist(fullpath) and job.has_key?("Label") - if job["Label"] == label - return { label => fullpath } - else - label_to_path_map[job["Label"]] = fullpath - end + @label_to_path_map ||= {} + if @label_to_path_map.empty? + Launchd_Paths.each do |path| + Dir.glob(File.join(path,'*')).each do |filepath| + next if ! File.file?(filepath) + job = read_plist(filepath) + if job.has_key?("Label") and job["Label"] == label + return { label => filepath } + else + @label_to_path_map[job["Label"]] = filepath end end end end - # if we didn't find the job above and we should have, error. - raise Puppet::Error.new("Unable to find launchd plist for job: #{label}") if label - # if returning all jobs - label_to_path_map + if label + if @label_to_path_map.has_key? label + return { label => @label_to_path_map[label] } + else + raise Puppet::Error.new("Unable to find launchd plist for job: #{label}") + end + else + @label_to_path_map + end end - - def self.instances - jobs = self.jobsearch - jobs.keys.collect do |job| - new(:name => job, :provider => :launchd, :path => jobs[job]) + # This status method lists out all currently running services. + # This hash is returned at the end of the method. + def self.job_list + @job_list = Hash.new + begin + output = launchctl :list + raise Puppet::Error.new("launchctl list failed to return any data.") if output.nil? + output.split("\n").each do |line| + @job_list[line.split(/\s/).last] = :running + end + rescue Puppet::ExecutionFailure + raise Puppet::Error.new("Unable to determine status of #{resource[:name]}") end + @job_list + end + + # Launchd implemented plist overrides in version 10.6. + # This method checks the major_version of OS X and returns true if + # it is 10.6 or greater. This allows us to implement different plist + # behavior for versions >= 10.6 + def has_macosx_plist_overrides? + @product_version ||= self.class.get_macosx_version_major + return true unless /^10\.[0-5]/.match(@product_version) + return false + end + + # Read a plist, whether its format is XML or in Apple's "binary1" + # format. + def self.read_plist(path) + Plist::parse_xml(plutil('-convert', 'xml1', '-o', '/dev/stdout', path)) + end + + # Clean out the @property_hash variable containing the cached list of services + def flush + @property_hash.clear end + def exists? + Puppet.debug("Puppet::Provider::Launchd:Ensure for #{@property_hash[:name]}: #{@property_hash[:ensure]}") + @property_hash[:ensure] != :absent + end def self.get_macosx_version_major - return @macosx_version_major if defined?(@macosx_version_major) + return @macosx_version_major if @macosx_version_major begin # Make sure we've loaded all of the facts Facter.loadfacts - if Facter.value(:macosx_productversion_major) product_version_major = Facter.value(:macosx_productversion_major) else # TODO: remove this code chunk once we require Facter 1.5.5 or higher. - Puppet.warning("DEPRECATION WARNING: Future versions of the launchd provider will require Facter 1.5.5 or newer.") + warnonce("DEPRECATION WARNING: Future versions of the launchd provider will require Facter 1.5.5 or newer.") product_version = Facter.value(:macosx_productversion) fail("Could not determine OS X version from Facter") if product_version.nil? product_version_major = product_version.scan(/(\d+)\.(\d+)./).join(".") end fail("#{product_version_major} is not supported by the launchd provider") if %w{10.0 10.1 10.2 10.3}.include?(product_version_major) @macosx_version_major = product_version_major return @macosx_version_major rescue Puppet::ExecutionFailure => detail fail("Could not determine OS X version: #{detail}") end end # finds the path for a given label and returns the path and parsed plist # as an array of [path, plist]. Note plist is really a Hash here. def plist_from_label(label) job = self.class.jobsearch(label) job_path = job[label] if FileTest.file?(job_path) job_plist = self.class.read_plist(job_path) else raise Puppet::Error.new("Unable to parse launchd plist at path: #{job_path}") end [job_path, job_plist] end - - def status - # launchctl list exits zero if the job is loaded - # and non-zero if it isn't. Simple way to check... but is only - # available on OS X 10.5 unfortunately, so we grab the whole list - # and check if our resource is included. The output formats differ - # between 10.4 and 10.5, thus the necessity for splitting - begin - output = launchctl :list - raise Puppet::Error.new("launchctl list failed to return any data.") if output.nil? - output.split("\n").each do |j| - return :running if j.split(/\s/).last == resource[:name] - end - return :stopped - rescue Puppet::ExecutionFailure - raise Puppet::Error.new("Unable to determine status of #{resource[:name]}") - end - end - - # start the service. To get to a state of running/enabled, we need to # conditionally enable at load, then disable by modifying the plist file # directly. def start job_path, job_plist = plist_from_label(resource[:name]) did_enable_job = false cmds = [] cmds << :launchctl << :load if self.enabled? == :false # launchctl won't load disabled jobs cmds << "-w" did_enable_job = true end cmds << job_path begin execute(cmds) rescue Puppet::ExecutionFailure raise Puppet::Error.new("Unable to start service: #{resource[:name]} at path: #{job_path}") end # As load -w clears the Disabled flag, we need to add it in after self.disable if did_enable_job and resource[:enable] == :false end def stop job_path, job_plist = plist_from_label(resource[:name]) did_disable_job = false cmds = [] cmds << :launchctl << :unload if self.enabled? == :true # keepalive jobs can't be stopped without disabling cmds << "-w" did_disable_job = true end cmds << job_path begin execute(cmds) rescue Puppet::ExecutionFailure raise Puppet::Error.new("Unable to stop service: #{resource[:name]} at path: #{job_path}") end # As unload -w sets the Disabled flag, we need to add it in after self.enable if did_disable_job and resource[:enable] == :true end # launchd jobs are enabled by default. They are only disabled if the key # "Disabled" is set to true, but it can also be set to false to enable it. - # In 10.6, the Disabled key in the job plist is consulted, but only if there - # is no entry in the global overrides plist. + # Starting in 10.6, the Disabled key in the job plist is consulted, but only + # if there is no entry in the global overrides plist. # We need to draw a distinction between undefined, true and false for both # locations where the Disabled flag can be defined. def enabled? job_plist_disabled = nil overrides_disabled = nil job_path, job_plist = plist_from_label(resource[:name]) job_plist_disabled = job_plist["Disabled"] if job_plist.has_key?("Disabled") - if self.class.get_macosx_version_major == "10.6" + if has_macosx_plist_overrides? if FileTest.file?(Launchd_Overrides) and overrides = self.class.read_plist(Launchd_Overrides) if overrides.has_key?(resource[:name]) overrides_disabled = overrides[resource[:name]]["Disabled"] if overrides[resource[:name]].has_key?("Disabled") end end end if overrides_disabled.nil? if job_plist_disabled.nil? or job_plist_disabled == false return :true end elsif overrides_disabled == false return :true end :false end # enable and disable are a bit hacky. We write out the plist with the appropriate value # rather than dealing with launchctl as it is unable to change the Disabled flag # without actually loading/unloading the job. - # In 10.6 we need to write out a disabled key to the global overrides plist, in earlier - # versions this is stored in the job plist itself. + # Starting in 10.6 we need to write out a disabled key to the global + # overrides plist, in earlier versions this is stored in the job plist itself. def enable - if self.class.get_macosx_version_major == "10.6" + if has_macosx_plist_overrides? overrides = self.class.read_plist(Launchd_Overrides) overrides[resource[:name]] = { "Disabled" => false } Plist::Emit.save_plist(overrides, Launchd_Overrides) else job_path, job_plist = plist_from_label(resource[:name]) if self.enabled? == :false job_plist.delete("Disabled") Plist::Emit.save_plist(job_plist, job_path) end end end def disable - if self.class.get_macosx_version_major == "10.6" + if has_macosx_plist_overrides? overrides = self.class.read_plist(Launchd_Overrides) overrides[resource[:name]] = { "Disabled" => true } Plist::Emit.save_plist(overrides, Launchd_Overrides) else job_path, job_plist = plist_from_label(resource[:name]) job_plist["Disabled"] = true Plist::Emit.save_plist(job_plist, job_path) end end end diff --git a/spec/unit/provider/service/launchd_spec.rb b/spec/unit/provider/service/launchd_spec.rb index 8ae7f003d..c149c7e4f 100755 --- a/spec/unit/provider/service/launchd_spec.rb +++ b/spec/unit/provider/service/launchd_spec.rb @@ -1,203 +1,215 @@ -#!/usr/bin/env rspec -# -# Unit testing for the launchd service provider +# Spec Tests for the Launchd provider # require 'spec_helper' -require 'puppet' - -provider_class = Puppet::Type.type(:service).provider(:launchd) - -describe provider_class do - - before :each do - # Create a mock resource - @resource = stub 'resource' - - @provider = provider_class.new - @joblabel = "com.foo.food" - @jobplist = {} - - # A catch all; no parameters set - @resource.stubs(:[]).returns(nil) - - # But set name, ensure and enable - @resource.stubs(:[]).with(:name).returns @joblabel - @resource.stubs(:[]).with(:ensure).returns :enabled - @resource.stubs(:[]).with(:enable).returns :true - @resource.stubs(:ref).returns "Service[#{@joblabel}]" - - # stub out the provider methods that actually touch the filesystem - # or execute commands - @provider.stubs(:plist_from_label).returns([@joblabel, @jobplist]) - @provider.stubs(:execute).returns("") - @provider.stubs(:resource).returns @resource - - # We stub this out for the normal case as 10.6 is "special". - provider_class.stubs(:get_macosx_version_major).returns("10.5") - - end - - it "should have a start method for #{@provider.object_id}" do - @provider.should respond_to(:start) - end - - it "should have a stop method" do - @provider.should respond_to(:stop) - end +describe Puppet::Type.type(:service).provider(:launchd) do + let (:joblabel) { "com.foo.food" } + let (:provider) { subject.class } + let (:launchd_overrides) { '/var/db/launchd.db/com.apple.launchd/overrides.plist' } - it "should have an enabled? method" do - @provider.should respond_to(:enabled?) - end - - it "should have an enable method" do - @provider.should respond_to(:enable) - end - - it "should have a disable method" do - @provider.should respond_to(:disable) - end - - it "should have a status method" do - @provider.should respond_to(:status) + describe "the type interface" do + %w{ start stop enabled? enable disable status}.each do |method| + it { should respond_to method.to_sym } + end end - - describe "when checking status" do + describe 'the status of the services' do it "should call the external command 'launchctl list' once" do - @provider.expects(:launchctl).with(:list).returns("rotating-strawberry-madonnas") - @provider.status + provider.expects(:launchctl).with(:list).returns(joblabel) + provider.expects(:jobsearch).with(nil).returns({joblabel => "/Library/LaunchDaemons/#{joblabel}"}) + provider.prefetch({}) end it "should return stopped if not listed in launchctl list output" do - @provider.stubs(:launchctl).with(:list).returns("rotating-strawberry-madonnas") - @provider.status.should == :stopped + provider.expects(:launchctl).with(:list).returns('com.bar.is_running') + provider.expects(:jobsearch).with(nil).returns({'com.bar.is_not_running' => "/Library/LaunchDaemons/com.bar.is_not_running"}) + provider.prefetch({}).last.status.should eq :stopped end it "should return running if listed in launchctl list output" do - @provider.stubs(:launchctl).with(:list).returns(@joblabel) - @provider.status.should == :running + provider.expects(:launchctl).with(:list).returns('com.bar.is_running') + provider.expects(:jobsearch).with(nil).returns({'com.bar.is_running' => "/Library/LaunchDaemons/com.bar.is_running"}) + provider.prefetch({}).last.status.should eq :running + end + after :each do + provider.instance_variable_set(:@job_list, nil) end end - describe "when checking whether the service is enabled" do - it "should return true if the job plist says disabled is false" do - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) - @provider.enabled?.should == :true + describe "when checking whether the service is enabled on OS X 10.5" do + it "should return true in if the job plist says disabled is false" do + Facter.stubs(:value).with(:macosx_productversion_major).returns('10.5') + Facter.stubs(:value).with(:kernel).returns('Darwin') + Facter.stubs(:value).with(:macaddress).returns('') + Facter.stubs(:value).with(:arp).returns('') + subject.expects(:plist_from_label).with(joblabel).returns(["foo", {"Disabled" => false}]) + subject.expects(:resource).returns({:name => joblabel}) + subject.enabled?.should == :true end - it "should return true if the job plist has no disabled key" do - @provider.stubs(:plist_from_label).returns(["foo", {}]) - @provider.enabled?.should == :true + it "should return true in if the job plist has no disabled key" do + subject.expects(:resource).returns({:name => joblabel}) + subject.stubs(:plist_from_label).returns(["foo", {}]) + subject.enabled?.should == :true end - it "should return false if the job plist says disabled is true" do - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) - @provider.enabled?.should == :false + it "should return false in if the job plist says disabled is true" do + subject.expects(:resource).returns({:name => joblabel}) + subject.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) + subject.enabled?.should == :false end end describe "when checking whether the service is enabled on OS X 10.6" do it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) - @provider.class.stubs(:read_plist).returns({@resource[:name] => {"Disabled" => false}}) - FileTest.expects(:file?).with(Launchd_Overrides).returns(true) - @provider.enabled?.should == :true + provider.expects(:get_macosx_version_major).returns("10.6") + subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => true}]) + provider.stubs(:read_plist).returns({joblabel => {"Disabled" => false}}) + FileTest.expects(:file?).with(launchd_overrides).returns(true) + subject.stubs(:resource).returns({:name => joblabel}) + subject.enabled?.should == :true end it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) - @provider.class.stubs(:read_plist).returns({@resource[:name] => {"Disabled" => true}}) - FileTest.expects(:file?).with(Launchd_Overrides).returns(true) - @provider.enabled?.should == :false + provider.expects(:get_macosx_version_major).returns("10.6") + subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => false}]) + provider.stubs(:read_plist).returns({joblabel => {"Disabled" => true}}) + FileTest.expects(:file?).with(launchd_overrides).returns(true) + subject.stubs(:resource).returns({:name => joblabel}) + subject.enabled?.should == :false end it "should return true if the job plist and the global overrides have no disabled keys" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.stubs(:plist_from_label).returns(["foo", {}]) - @provider.class.stubs(:read_plist).returns({}) - FileTest.expects(:file?).with(Launchd_Overrides).returns(true) - @provider.enabled?.should == :true + provider.expects(:get_macosx_version_major).returns("10.6") + subject.expects(:plist_from_label).returns([joblabel, {}]) + provider.stubs(:read_plist).returns({}) + FileTest.expects(:file?).with(launchd_overrides).returns(true) + subject.stubs(:resource).returns({:name => joblabel}) + subject.enabled?.should == :true end end describe "when starting the service" do it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.start - end - it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:execute).with([:launchctl, :load, @resource[:name]]).once - @provider.start + subject.expects(:plist_from_label).returns([joblabel, {}]).once + subject.stubs(:enabled?).returns :true + subject.stubs(:execute).with([:launchctl, :load, joblabel]) + subject.stubs(:resource).returns({:name => joblabel}) + subject.start + end + it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do + subject.stubs(:plist_from_label).returns([joblabel, {}]) + subject.stubs(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :load, joblabel]).once + subject.stubs(:resource).returns({:name => joblabel}) + subject.start end it "should execute 'launchctl load' with writing to the plist once if the job is disabled" do - @provider.stubs(:enabled?).returns :false - @provider.expects(:execute).with([:launchctl, :load, "-w", @resource[:name]]).once - @provider.start + subject.stubs(:plist_from_label).returns([joblabel, {}]) + subject.stubs(:enabled?).returns(:false) + subject.stubs(:resource).returns({:name => joblabel}) + subject.expects(:execute).with([:launchctl, :load, "-w", joblabel]).once + subject.start end it "should disable the job once if the job is disabled and should be disabled at boot" do - @provider.stubs(:enabled?).returns :false - @resource.stubs(:[]).with(:enable).returns :false - @provider.expects(:disable).once - @provider.start + subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => true}]) + subject.stubs(:enabled?).returns :false + subject.stubs(:execute).with([:launchctl, :load, "-w", joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :false}) + subject.expects(:disable).once + subject.start end end describe "when stopping the service" do it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop + subject.expects(:plist_from_label).returns([joblabel, {}]).once + subject.stubs(:enabled?).returns :true + subject.stubs(:execute).with([:launchctl, :unload, '-w', joblabel]) + subject.stubs(:resource).returns({:name => joblabel}) + subject.stop end it "should execute 'launchctl unload' once without writing to the plist if the job is disabled" do - @provider.stubs(:enabled?).returns :false - @provider.expects(:execute).with([:launchctl, :unload, @resource[:name]]).once - @provider.stop + subject.stubs(:plist_from_label).returns([joblabel, {}]) + subject.stubs(:enabled?).returns :false + subject.expects(:execute).with([:launchctl, :unload, joblabel]).once + subject.stubs(:resource).returns({:name => joblabel}) + subject.stop end it "should execute 'launchctl unload' with writing to the plist once if the job is enabled" do - @provider.stubs(:enabled?).returns :true - @provider.expects(:execute).with([:launchctl, :unload, "-w", @resource[:name]]).once - @provider.stop + subject.stubs(:plist_from_label).returns([joblabel, {}]) + subject.stubs(:enabled?).returns :true + subject.expects(:execute).with([:launchctl, :unload, '-w', joblabel]).once + subject.stubs(:resource).returns({:name => joblabel}) + subject.stop end it "should enable the job once if the job is enabled and should be enabled at boot" do - @provider.stubs(:enabled?).returns :true - @resource.stubs(:[]).with(:enable).returns :true - @provider.expects(:enable).once - @provider.stop + subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => false}]) + subject.stubs(:enabled?).returns :true + subject.stubs(:execute).with([:launchctl, :unload, "-w", joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) + subject.expects(:enable).once + subject.stop end end describe "when enabling the service" do - it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop + it "should look for the relevant plist once" do ### Do we need this test? Differentiating it? + subject.expects(:plist_from_label).returns([joblabel, {}]).once + subject.stubs(:enabled?).returns :false + subject.stubs(:execute).with([:launchctl, :unload, joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) + subject.stop end it "should check if the job is enabled once" do - @provider.expects(:enabled?).once - @provider.stop + subject.stubs(:plist_from_label).returns([joblabel, {}]).once + subject.expects(:enabled?).once + subject.stubs(:execute).with([:launchctl, :unload, joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) + subject.stop end end describe "when disabling the service" do it "should look for the relevant plist once" do - @provider.expects(:plist_from_label).once - @provider.stop + subject.expects(:plist_from_label).returns([joblabel, {}]).once + subject.stubs(:enabled?).returns :true + subject.stubs(:execute).with([:launchctl, :unload, '-w', joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :false}) + subject.stop end end describe "when enabling the service on OS X 10.6" do it "should write to the global launchd overrides file once" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.class.stubs(:read_plist).returns({}) + provider.stubs(:get_macosx_version_major).returns("10.6") + provider.stubs(:read_plist).returns({}) Plist::Emit.expects(:save_plist).once - @provider.enable + subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) + subject.enable end end describe "when disabling the service on OS X 10.6" do it "should write to the global launchd overrides file once" do - provider_class.stubs(:get_macosx_version_major).returns("10.6") - @provider.class.stubs(:read_plist).returns({}) + provider.stubs(:get_macosx_version_major).returns("10.6") + provider.stubs(:read_plist).returns({}) Plist::Emit.expects(:save_plist).once - @provider.enable + subject.stubs(:resource).returns({:name => joblabel, :enable => :false}) + subject.enable end end -end + describe "when using an incompatible version of Facter" do + before :each do + provider.instance_variable_set(:@macosx_version_major, nil) + end + it "should display a deprecation warning" do + Facter.stubs(:value).with(:macosx_productversion_major).returns(nil) + Facter.stubs(:value).with(:kernel).returns('Darwin') + Facter.stubs(:value).with(:macosx_productversion).returns('10.5.8') + Puppet::Util::Warnings.expects(:maybe_log) + provider.stubs(:read_plist).returns({joblabel => {"Disabled" => false}}) + subject.stubs(:plist_from_label).returns([joblabel, {"Disabled" => false}]) + subject.stubs(:enabled?).returns :false + subject.stubs(:execute).with([:launchctl, :load, '-w', joblabel]) + subject.stubs(:resource).returns({:name => joblabel, :enable => :true}) + subject.enable + end + end +end \ No newline at end of file