diff options
Diffstat (limited to 'spec/unit/provider/service')
-rw-r--r-- | spec/unit/provider/service/arch_service_spec.rb | 330 | ||||
-rw-r--r-- | spec/unit/provider/service/debian_service_spec.rb | 254 | ||||
-rw-r--r-- | spec/unit/provider/service/freebsd_service_spec.rb | 379 | ||||
-rw-r--r-- | spec/unit/provider/service/gentoo_service_spec.rb | 144 | ||||
-rw-r--r-- | spec/unit/provider/service/init_service_spec.rb | 212 | ||||
-rw-r--r-- | spec/unit/provider/service/insserv_service_spec.rb | 76 | ||||
-rw-r--r-- | spec/unit/provider/service/invokercd_service_spec.rb | 212 | ||||
-rw-r--r-- | spec/unit/provider/service/macosx_spec.rb | 229 | ||||
-rw-r--r-- | spec/unit/provider/service/redhat_spec.rb | 156 | ||||
-rw-r--r-- | spec/unit/provider/service/simple_service_spec.rb | 171 | ||||
-rw-r--r-- | spec/unit/provider/service/solaris_smf_service_spec.rb | 140 | ||||
-rw-r--r-- | spec/unit/provider/service/systemd_service_spec.rb | 239 | ||||
-rw-r--r-- | spec/unit/provider/service/upstart_service_spec.rb | 314 | ||||
-rw-r--r-- | spec/unit/provider/service/windows_spec.rb | 235 |
14 files changed, 3091 insertions, 0 deletions
diff --git a/spec/unit/provider/service/arch_service_spec.rb b/spec/unit/provider/service/arch_service_spec.rb new file mode 100644 index 0000000000..a7afa28da1 --- /dev/null +++ b/spec/unit/provider/service/arch_service_spec.rb @@ -0,0 +1,330 @@ +# +# Author:: Jan Zimmek (<jan.zimmek@web.de>) +# Author:: AJ Christensen (<aj@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' +require 'ostruct' + + +# most of this code has been ripped from init_service_spec.rb +# and is only slightly modified to match "arch" needs. + +describe Chef::Provider::Service::Arch, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => "ps -ef"} + + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + @new_resource.pattern("chef") + @new_resource.supports({:status => false}) + + + @provider = Chef::Provider::Service::Arch.new(@new_resource, @run_context) + + ::File.stub!(:exists?).with("/etc/rc.conf").and_return(true) + ::File.stub!(:read).with("/etc/rc.conf").and_return("DAEMONS=(network apache sshd)") + end + + describe "when first created" do + it "should set the current resources service name to the new resources service name" do + @provider.stub(:shell_out).and_return(OpenStruct.new(:exitstatus => 0, :stdout => "")) + @provider.load_current_resource + @provider.current_resource.service_name.should == 'chef' + end + end + + + describe "when the service supports status" do + before do + @new_resource.supports({:status => true}) + end + + it "should run '/etc/rc.d/service_name status'" do + @provider.should_receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0)) + @provider.load_current_resource + end + + it "should set running to true if the the status command returns 0" do + @provider.stub!(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0)) + @provider.load_current_resource + @provider.current_resource.running.should be_true + end + + it "should set running to false if the status command returns anything except 0" do + @provider.stub!(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 1)) + @provider.load_current_resource + @provider.current_resource.running.should be_false + end + + it "should set running to false if the status command raises" do + @provider.stub!(:shell_out).with("/etc/rc.d/chef status").and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @provider.current_resource.running.should be_false + end + + end + + + describe "when a status command has been specified" do + before do + @new_resource.status_command("/etc/rc.d/chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.should_receive(:shell_out).with("/etc/rc.d/chefhasmonkeypants status").and_return(OpenStruct.new(:exitstatus => 0)) + @provider.load_current_resource + end + + end + + it "should raise error if the node has a nil ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => nil} + @provider.define_resource_requirements + @provider.action = :start + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should raise error if the node has an empty ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => ""} + @provider.define_resource_requirements + @provider.action = :start + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + + it "should fail if file /etc/rc.conf does not exist" do + ::File.stub!(:exists?).with("/etc/rc.conf").and_return(false) + lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Service) + end + + it "should fail if file /etc/rc.conf does not contain DAEMONS array" do + ::File.stub!(:read).with("/etc/rc.conf").and_return("") + lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Service) + end + + describe "when discovering service status with ps" do + before do + @stdout = StringIO.new(<<-DEFAULT_PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb +aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash +aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb +DEFAULT_PS + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + + @node.automatic_attrs[:command] = {:ps => "ps -ef"} + end + + it "determines the service is running when it appears in ps" do + @stdout = StringIO.new(<<-RUNNING_PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 chef +aj 7842 5057 0 21:26 pts/2 00:00:06 poos +RUNNING_PS + @status.stub!(:stdout).and_return(@stdout) + @provider.load_current_resource + @provider.current_resource.running.should be_true + end + + it "determines the service is not running when it does not appear in ps" do + @provider.stub!(:shell_out!).and_return(@status) + @provider.load_current_resource + @provider.current_resource.running.should be_false + end + + it "should raise an exception if ps fails" do + @provider.stub!(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + it "should return existing entries in DAEMONS array" do + ::File.stub!(:read).with("/etc/rc.conf").and_return("DAEMONS=(network !apache ssh)") + @provider.daemons.should == ['network', '!apache', 'ssh'] + end + + context "when the current service status is known" do + before do + @current_resource = Chef::Resource::Service.new("chef") + @provider.current_resource = @current_resource + end + + describe Chef::Provider::Service::Arch, "enable_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:start_command).and_return(false) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should add chef to DAEMONS array" do + ::File.stub!(:read).with("/etc/rc.conf").and_return("DAEMONS=(network)") + @provider.should_receive(:update_daemons).with(['network', 'chef']) + @provider.enable_service() + end + end + + describe Chef::Provider::Service::Arch, "disable_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:start_command).and_return(false) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should remove chef from DAEMONS array" do + ::File.stub!(:read).with("/etc/rc.conf").and_return("DAEMONS=(network chef)") + @provider.should_receive(:update_daemons).with(['network', '!chef']) + @provider.disable_service() + end + end + + + describe Chef::Provider::Service::Arch, "start_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:start_command).and_return(false) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should call the start command if one is specified" do + @new_resource.stub!(:start_command).and_return("/etc/rc.d/chef startyousillysally") + @provider.should_receive(:shell_out!).with("/etc/rc.d/chef startyousillysally") + @provider.start_service() + end + + it "should call '/etc/rc.d/service_name start' if no start command is specified" do + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} start") + @provider.start_service() + end + end + + describe Chef::Provider::Service::Arch, "stop_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:stop_command).and_return(false) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should call the stop command if one is specified" do + @new_resource.stub!(:stop_command).and_return("/etc/rc.d/chef itoldyoutostop") + @provider.should_receive(:shell_out!).with("/etc/rc.d/chef itoldyoutostop") + @provider.stop_service() + end + + it "should call '/etc/rc.d/service_name stop' if no stop command is specified" do + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} stop") + @provider.stop_service() + end + end + + describe Chef::Provider::Service::Arch, "restart_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:restart_command).and_return(false) + # @new_resource.stub!(:supports).and_return({:restart => false}) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should call 'restart' on the service_name if the resource supports it" do + @new_resource.stub!(:supports).and_return({:restart => true}) + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} restart") + @provider.restart_service() + end + + it "should call the restart_command if one has been specified" do + @new_resource.stub!(:restart_command).and_return("/etc/rc.d/chef restartinafire") + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} restartinafire") + @provider.restart_service() + end + + it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do + @provider.should_receive(:stop_service) + @provider.should_receive(:sleep).with(1) + @provider.should_receive(:start_service) + @provider.restart_service() + end + end + + describe Chef::Provider::Service::Arch, "reload_service" do + # before(:each) do + # @new_resource = mock("Chef::Resource::Service", + # :null_object => true, + # :name => "chef", + # :service_name => "chef", + # :running => false + # ) + # @new_resource.stub!(:reload_command).and_return(false) + # @new_resource.stub!(:supports).and_return({:reload => false}) + # + # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) + # Chef::Resource::Service.stub!(:new).and_return(@current_resource) + # end + + it "should call 'reload' on the service if it supports it" do + @new_resource.stub!(:supports).and_return({:reload => true}) + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} reload") + @provider.reload_service() + end + + it "should should run the user specified reload command if one is specified and the service doesn't support reload" do + @new_resource.stub!(:reload_command).and_return("/etc/rc.d/chef lollerpants") + @provider.should_receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} lollerpants") + @provider.reload_service() + end + end + end +end diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb new file mode 100644 index 0000000000..bea9360561 --- /dev/null +++ b/spec/unit/provider/service/debian_service_spec.rb @@ -0,0 +1,254 @@ +# +# Author:: AJ Christensen (<aj@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 HJK Solutions, LLC +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Debian, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => 'fuuuu'} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Debian.new(@new_resource, @run_context) + @provider.current_resource = @current_resource + + @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil + + end + + it "ensures /usr/sbin/update-rc.d is available" do + File.should_receive(:exists?).with("/usr/sbin/update-rc.d").and_return(false) + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements } .should raise_error(Chef::Exceptions::Service) + end + + describe "when update-rc.d shows the init script linked to rc*.d/" do + before do + @provider.stub!(:assert_update_rcd_available) + + result=<<-UPDATE_RC_D_SUCCESS +Removing any system startup links for /etc/init.d/chef ... + /etc/rc0.d/K20chef + /etc/rc1.d/K20chef + /etc/rc2.d/S20chef + /etc/rc3.d/S20chef + /etc/rc4.d/S20chef + /etc/rc5.d/S20chef + /etc/rc6.d/K20chef + UPDATE_RC_D_SUCCESS + @stdout = StringIO.new(result) + @stderr = StringIO.new + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + end + + it "says the service is enabled" do + @provider.service_currently_enabled?(@provider.get_priority).should be_true + end + + it "stores the 'enabled' state" do + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.load_current_resource.should equal(@current_resource) + @current_resource.enabled.should be_true + end + end + + {"Debian/Lenny and older" => { + "linked" => { + "stdout" => " Removing any system startup links for /etc/init.d/chef ... + /etc/rc0.d/K20chef + /etc/rc1.d/K20chef + /etc/rc2.d/S20chef + /etc/rc3.d/S20chef + /etc/rc4.d/S20chef + /etc/rc5.d/S20chef + /etc/rc6.d/K20chef", + "stderr" => "" + }, + "not linked" => { + "stdout" => " Removing any system startup links for /etc/init.d/chef ...", + "stderr" => "" + }, + }, + "Debian/Squeeze and earlier" => { + "linked" => { + "stdout" => "update-rc.d: using dependency based boot sequencing", + "stderr" => "insserv: remove service /etc/init.d/../rc0.d/K20chef-client +insserv: remove service /etc/init.d/../rc1.d/K20chef-client +insserv: remove service /etc/init.d/../rc2.d/S20chef-client +insserv: remove service /etc/init.d/../rc3.d/S20chef-client +insserv: remove service /etc/init.d/../rc4.d/S20chef-client +insserv: remove service /etc/init.d/../rc5.d/S20chef-client +insserv: remove service /etc/init.d/../rc6.d/K20chef-client +insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop" + }, + "not linked" => { + "stdout" => "update-rc.d: using dependency based boot sequencing", + "stderr" => "" + } + } + }.each do |model, streams| + describe "when update-rc.d shows the init script linked to rc*.d/" do + before do + @provider.stub!(:assert_update_rcd_available) + + @stdout = StringIO.new(streams["linked"]["stdout"]) + @stderr = StringIO.new(streams["linked"]["stderr"]) + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + end + + it "says the service is enabled" do + @provider.service_currently_enabled?(@provider.get_priority).should be_true + end + + it "stores the 'enabled' state" do + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.load_current_resource.should equal(@current_resource) + @current_resource.enabled.should be_true + end + + it "stores the start/stop priorities of the service" do + @provider.load_current_resource + expected_priorities = {"6"=>[:stop, "20"], + "0"=>[:stop, "20"], + "1"=>[:stop, "20"], + "2"=>[:start, "20"], + "3"=>[:start, "20"], + "4"=>[:start, "20"], + "5"=>[:start, "20"]} + @provider.current_resource.priority.should == expected_priorities + end + end + + describe "when using squeeze/earlier and update-rc.d shows the init script isn't linked to rc*.d" do + before do + @provider.stub!(:assert_update_rcd_available) + @stdout = StringIO.new(streams["not linked"]["stdout"]) + @stderr = StringIO.new(streams["not linked"]["stderr"]) + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + end + + it "says the service is disabled" do + @provider.service_currently_enabled?(@provider.get_priority).should be_false + end + + it "stores the 'disabled' state" do + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.load_current_resource.should equal(@current_resource) + @current_resource.enabled.should be_false + end + end + end + + describe "when update-rc.d shows the init script isn't linked to rc*.d" do + before do + @provider.stub!(:assert_update_rcd_available) + @status = mock("Status", :exitstatus => 0) + @stdout = StringIO.new(" Removing any system startup links for /etc/init.d/chef ...") + @stderr = StringIO.new + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + end + + it "says the service is disabled" do + @provider.service_currently_enabled?(@provider.get_priority).should be_false + end + + it "stores the 'disabled' state" do + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.load_current_resource.should equal(@current_resource) + @current_resource.enabled.should be_false + end + end + + describe "when update-rc.d fails" do + before do + @status = mock("Status", :exitstatus => -1) + @provider.stub!(:popen4).and_return(@status) + end + + it "raises an error" do + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + describe "when enabling a service without priority" do + it "should call update-rc.d 'service_name' defaults" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove"}) + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults"}) + @provider.enable_service() + end + end + + describe "when enabling a service with simple priority" do + before do + @new_resource.priority(75) + end + + it "should call update-rc.d 'service_name' defaults" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove"}) + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults 75 25"}) + @provider.enable_service() + end + end + + describe "when enabling a service with complex priorities" do + before do + @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55]) + end + + it "should call update-rc.d 'service_name' defaults" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove"}) + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} start 20 2 . stop 55 3 . "}) + @provider.enable_service() + end + end + + describe "when disabling a service without a priority" do + + it "should call update-rc.d -f 'service_name' remove + stop with a default priority" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove"}) + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop 80 2 3 4 5 ."}) + @provider.disable_service() + end + end + + describe "when disabling a service with simple priority" do + before do + @new_resource.priority(75) + end + + it "should call update-rc.d -f 'service_name' remove + stop with a specified priority" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove"}) + @provider.should_receive(:run_command).with({:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop #{100 - @new_resource.priority} 2 3 4 5 ."}) + @provider.disable_service() + end + end +end diff --git a/spec/unit/provider/service/freebsd_service_spec.rb b/spec/unit/provider/service/freebsd_service_spec.rb new file mode 100644 index 0000000000..6dd06bde2c --- /dev/null +++ b/spec/unit/provider/service/freebsd_service_spec.rb @@ -0,0 +1,379 @@ +# +# Author:: Bryan McLellan (btm@loftninjas.org) +# Copyright:: Copyright (c) 2009 Bryan McLellan +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Freebsd do + before do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => "ps -ax"} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("apache22") + @new_resource.pattern("httpd") + @new_resource.supports({:status => false}) + + @current_resource = Chef::Resource::Service.new("apache22") + + @provider = Chef::Provider::Service::Freebsd.new(@new_resource,@run_context) + @provider.action = :start + @init_command = "/usr/local/etc/rc.d/apache22" + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + end + + describe "load_current_resource" do + before(:each) do + @stdout = StringIO.new(<<-PS_SAMPLE) +413 ?? Ss 0:02.51 /usr/sbin/syslogd -s +539 ?? Is 0:00.14 /usr/sbin/sshd +545 ?? Ss 0:17.53 sendmail: accepting connections (sendmail) +PS_SAMPLE + @status = mock(:stdout => @stdout, :exitstatus => 0) + @provider.stub!(:shell_out!).with(@node[:command][:ps]).and_return(@status) + + ::File.stub!(:exists?).and_return(false) + ::File.stub!(:exists?).with("/usr/local/etc/rc.d/#{@new_resource.service_name}").and_return(true) + @lines = mock("lines") + @lines.stub!(:each).and_yield("sshd_enable=\"YES\""). + and_yield("#{@new_resource.name}_enable=\"YES\"") + ::File.stub!(:open).and_return(@lines) + + @rc_with_name = StringIO.new(<<-RC_SAMPLE) +name="apache22" +rcvar=`set_rcvar` +RC_SAMPLE + ::File.stub!(:open).with("/usr/local/etc/rc.d/#{@new_resource.service_name}").and_return(@rc_with_name) + @provider.stub(:service_enable_variable_name).and_return nil + + end + + it "should create a current resource with the name of the new resource" do + Chef::Resource::Service.should_receive(:new).and_return(@current_resource) + @provider.load_current_resource + end + + it "should set the current resources service name to the new resources service name" do + @provider.load_current_resource + @current_resource.service_name.should == @new_resource.service_name + end + + it "should not raise an exception if the rcscript have a name variable" do + @provider.load_current_resource + lambda { @provider.service_enable_variable_name }.should_not raise_error(Chef::Exceptions::Service) + end + + describe "when the service supports status" do + before do + @new_resource.supports({:status => true}) + end + + it "should run '/etc/init.d/service_name status'" do + @provider.should_receive(:shell_out).with("/usr/local/etc/rc.d/#{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + end + + it "should set running to true if the the status command returns 0" do + @provider.should_receive(:shell_out).with("/usr/local/etc/rc.d/#{@current_resource.service_name} status").and_return(@status) + @current_resource.should_receive(:running).with(true) + @provider.load_current_resource + end + + it "should set running to false if the status command returns anything except 0" do + @provider.should_receive(:shell_out).with("/usr/local/etc/rc.d/#{@current_resource.service_name} status").and_raise(Mixlib::ShellOut::ShellCommandFailed) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + # @provider.current_resource.running.should be_false + end + end + + describe "when a status command has been specified" do + before do + @new_resource.status_command("/bin/chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.should_receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(@status) + @provider.load_current_resource + end + + end + + it "should raise error if the node has a nil ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => nil} + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should raise error if the node has an empty ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => ""} + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + describe "when executing assertions" do + it "should verify that /etc/rc.conf exists" do + ::File.should_receive(:exists?).with("/etc/rc.conf") + @provider.stub!(:service_enable_variable_name).and_return("#{@current_resource.service_name}_enable") + @provider.load_current_resource + end + + context "and the init script is not found" do + [ "start", "reload", "restart", "enable" ].each do |action| + it "should raise an exception when the action is #{action}" do + ::File.stub!(:exists?).and_return(false) + @provider.load_current_resource + @provider.define_resource_requirements + @provider.instance_variable_get("@rcd_script_found").should be_false + @provider.action = action + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + [ "stop", "disable" ].each do |action| + it "should not raise an error when the action is #{action}" do + @provider.action = action + lambda { @provider.process_resource_requirements }.should_not raise_error + end + end + end + + it "update state when current resource enabled state could not be determined" do + ::File.should_receive(:exists?).with("/etc/rc.conf").and_return false + @provider.load_current_resource + @provider.instance_variable_get("@enabled_state_found").should be_false + end + + it "update state when current resource enabled state could be determined" do + ::File.stub!(:exist?).with("/usr/local/etc/rc.d/#{@new_resource.service_name}").and_return(true) + ::File.should_receive(:exists?).with("/etc/rc.conf").and_return true + @provider.load_current_resource + @provider.instance_variable_get("@enabled_state_found").should be_false + @provider.instance_variable_get("@rcd_script_found").should be_true + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service, + "Could not find the service name in /usr/local/etc/rc.d/#{@current_resource.service_name} and rcvar") + end + + it "should throw an exception if service line is missing from rc.d script" do + pending "not implemented" do + false.should be_true + end + end + + end + + describe "when we have a 'ps' attribute" do + before do + @node.automatic_attrs[:command] = {:ps => "ps -ax"} + end + + it "should shell_out! the node's ps command" do + @provider.should_receive(:shell_out!).with(@node[:command][:ps]).and_return(@status) + @provider.load_current_resource + end + + it "should read stdout of the ps command" do + @provider.stub!(:shell_out!).and_return(@status) + @stdout.should_receive(:each_line).and_return(true) + @provider.load_current_resource + end + + it "should set running to true if the regex matches the output" do + @stdout.stub!(:each_line).and_yield("555 ?? Ss 0:05.16 /usr/sbin/cron -s"). + and_yield(" 9881 ?? Ss 0:06.67 /usr/local/sbin/httpd -DNOHTTPACCEPT") + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the regex doesn't match" do + @provider.stub!(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should raise an exception if ps fails" do + @provider.stub!(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + + describe "when starting the service" do + it "should call the start command if one is specified" do + @new_resource.start_command("/etc/rc.d/chef startyousillysally") + @provider.should_receive(:shell_out!).with("/etc/rc.d/chef startyousillysally") + @provider.load_current_resource + @provider.start_service() + end + + it "should call '/usr/local/etc/rc.d/service_name faststart' if no start command is specified" do + @provider.should_receive(:shell_out!).with("/usr/local/etc/rc.d/#{@new_resource.service_name} faststart") + @provider.load_current_resource + @provider.start_service() + end + end + + describe Chef::Provider::Service::Init, "stop_service" do + it "should call the stop command if one is specified" do + @new_resource.stop_command("/etc/init.d/chef itoldyoutostop") + @provider.should_receive(:shell_out!).with("/etc/init.d/chef itoldyoutostop") + @provider.load_current_resource + @provider.stop_service() + end + + it "should call '/usr/local/etc/rc.d/service_name faststop' if no stop command is specified" do + @provider.should_receive(:shell_out!).with("/usr/local/etc/rc.d/#{@new_resource.service_name} faststop") + @provider.load_current_resource + @provider.stop_service() + end + end + + describe "when restarting a service" do + it "should call 'restart' on the service_name if the resource supports it" do + @new_resource.supports({:restart => true}) + @provider.should_receive(:shell_out!).with("/usr/local/etc/rc.d/#{@new_resource.service_name} fastrestart") + @provider.load_current_resource + @provider.restart_service() + end + + it "should call the restart_command if one has been specified" do + @new_resource.restart_command("/etc/init.d/chef restartinafire") + @provider.should_receive(:shell_out!).with("/etc/init.d/chef restartinafire") + @provider.load_current_resource + @provider.restart_service() + end + end + + describe "when the rcscript does not have a name variable" do + before do + @rc_without_name = StringIO.new(<<-RC_SAMPLE) +rcvar=`set_rcvar` +RC_SAMPLE + ::File.stub!(:open).with("/usr/local/etc/rc.d/#{@current_resource.service_name}").and_return(@rc_with_noname) + @provider.current_resource = @current_resource + end + + describe "when rcvar returns foobar_enable" do + before do + @rcvar_stdout = <<RCVAR_SAMPLE +# apache22 +# +# #{@current_resource.service_name}_enable="YES" +# (default: "") +RCVAR_SAMPLE + @status = mock(:stdout => @rcvar_stdout, :exitstatus => 0) + @provider.stub!(:shell_out!).with("/usr/local/etc/rc.d/#{@current_resource.service_name} rcvar").and_return(@status) + end + + it "should get the service name from rcvar if the rcscript does not have a name variable" do + @provider.load_current_resource + @provider.unstub!(:service_enable_variable_name) + @provider.service_enable_variable_name.should == "#{@current_resource.service_name}_enable" + end + + it "should not raise an exception if the rcscript does not have a name variable" do + @provider.load_current_resource + lambda { @provider.service_enable_variable_name }.should_not raise_error(Chef::Exceptions::Service) + end + end + + describe "when rcvar does not return foobar_enable" do + before do + @rcvar_stdout = <<RCVAR_SAMPLE +# service_with_noname +# +RCVAR_SAMPLE + @status = mock(:stdout => @rcvar_stdout, :exitstatus => 0) + @provider.stub!(:shell_out!).with("/usr/local/etc/rc.d/#{@current_resource.service_name} rcvar").and_return(@status) + end + + [ "start", "reload", "restart", "enable" ].each do |action| + it "should raise an exception when the action is #{action}" do + @provider.action = action + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + [ "stop", "disable" ].each do |action| + it "should not raise an error when the action is #{action}" do + ::File.stub!(:exist?).with("/usr/local/etc/rc.d/#{@new_resource.service_name}").and_return(true) + @provider.action = action + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should_not raise_error(Chef::Exceptions::Service) + end + end + end + end + end + + describe Chef::Provider::Service::Freebsd, "enable_service" do + before do + @provider.current_resource = @current_resource + @provider.stub!(:service_enable_variable_name).and_return("#{@current_resource.service_name}_enable") + end + + it "should enable the service if it is not enabled" do + @current_resource.stub!(:enabled).and_return(false) + @provider.should_receive(:read_rc_conf).and_return([ "foo", "#{@current_resource.service_name}_enable=\"NO\"", "bar" ]) + @provider.should_receive(:write_rc_conf).with(["foo", "bar", "#{@current_resource.service_name}_enable=\"YES\""]) + @provider.enable_service() + end + + it "should enable the service if it is not enabled and not already specified in the rc.conf file" do + @current_resource.stub!(:enabled).and_return(false) + @provider.should_receive(:read_rc_conf).and_return([ "foo", "bar" ]) + @provider.should_receive(:write_rc_conf).with(["foo", "bar", "#{@current_resource.service_name}_enable=\"YES\""]) + @provider.enable_service() + end + + it "should not enable the service if it is already enabled" do + @current_resource.stub!(:enabled).and_return(true) + @provider.should_not_receive(:write_rc_conf) + @provider.enable_service + end + end + + describe Chef::Provider::Service::Freebsd, "disable_service" do + before do + @provider.current_resource = @current_resource + @provider.stub!(:service_enable_variable_name).and_return("#{@current_resource.service_name}_enable") + end + + it "should should disable the service if it is not disabled" do + @current_resource.stub!(:enabled).and_return(true) + @provider.should_receive(:read_rc_conf).and_return([ "foo", "#{@current_resource.service_name}_enable=\"YES\"", "bar" ]) + @provider.should_receive(:write_rc_conf).with(["foo", "bar", "#{@current_resource.service_name}_enable=\"NO\""]) + @provider.disable_service() + end + + it "should not disable the service if it is already disabled" do + @current_resource.stub!(:enabled).and_return(false) + @provider.should_not_receive(:write_rc_conf) + @provider.disable_service() + end + end +end diff --git a/spec/unit/provider/service/gentoo_service_spec.rb b/spec/unit/provider/service/gentoo_service_spec.rb new file mode 100644 index 0000000000..8d4ada043b --- /dev/null +++ b/spec/unit/provider/service/gentoo_service_spec.rb @@ -0,0 +1,144 @@ +# +# Author:: Lee Jensen (<ljensen@engineyard.com>) +# Author:: AJ Christensen (<aj@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Gentoo do + before(:each) do + @node = Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Gentoo.new(@new_resource, @run_context) + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out).and_return(@status) + File.stub!(:exists?).with("/etc/init.d/chef").and_return(true) + File.stub!(:exists?).with("/sbin/rc-update").and_return(true) + File.stub!(:exists?).with("/etc/runlevels/default/chef").and_return(false) + File.stub!(:readable?).with("/etc/runlevels/default/chef").and_return(false) + end + # new test: found_enabled state + # + describe "load_current_resource" do + it "should raise Chef::Exceptions::Service if /sbin/rc-update does not exist" do + File.should_receive(:exists?).with("/sbin/rc-update").and_return(false) + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should track when service file is not found in /etc/runlevels" do + @provider.load_current_resource + @provider.instance_variable_get("@found_script").should be_false + end + + it "should track when service file is found in /etc/runlevels/**/" do + Dir.stub!(:glob).with("/etc/runlevels/**/chef").and_return(["/etc/runlevels/default/chef"]) + @provider.load_current_resource + @provider.instance_variable_get("@found_script").should be_true + end + + describe "when detecting the service enable state" do + describe "and the glob returns a default service script file" do + before do + Dir.stub!(:glob).with("/etc/runlevels/**/chef").and_return(["/etc/runlevels/default/chef"]) + end + + describe "and the file exists and is readable" do + before do + File.stub!(:exists?).with("/etc/runlevels/default/chef").and_return(true) + File.stub!(:readable?).with("/etc/runlevels/default/chef").and_return(true) + end + it "should set enabled to true" do + @provider.load_current_resource + @current_resource.enabled.should be_true + end + end + + describe "and the file exists but is not readable" do + before do + File.stub!(:exists?).with("/etc/runlevels/default/chef").and_return(true) + File.stub!(:readable?).with("/etc/runlevels/default/chef").and_return(false) + end + + it "should set enabled to false" do + @provider.load_current_resource + @current_resource.enabled.should be_false + end + end + + describe "and the file does not exist" do + before do + File.stub!(:exists?).with("/etc/runlevels/default/chef").and_return(false) + File.stub!(:readable?).with("/etc/runlevels/default/chef").and_return("foobarbaz") + end + + it "should set enabled to false" do + @provider.load_current_resource + @current_resource.enabled.should be_false + end + + end + end + + end + + it "should return the current_resource" do + @provider.load_current_resource.should == @current_resource + end + + it "should support the status command automatically" do + @provider.load_current_resource + @new_resource.supports[:status].should be_true + end + + it "should support the restart command automatically" do + @provider.load_current_resource + @new_resource.supports[:restart].should be_true + end + + it "should not support the reload command automatically" do + @provider.load_current_resource + @new_resource.supports[:reload].should_not be_true + end + + end + + describe "action_methods" do + before(:each) { @provider.stub!(:load_current_resource).and_return(@current_resource) } + + describe Chef::Provider::Service::Gentoo, "enable_service" do + it "should call rc-update add *service* default" do + @provider.should_receive(:run_command).with({:command => "/sbin/rc-update add chef default"}) + @provider.enable_service() + end + end + + describe Chef::Provider::Service::Gentoo, "disable_service" do + it "should call rc-update del *service* default" do + @provider.should_receive(:run_command).with({:command => "/sbin/rc-update del chef default"}) + @provider.disable_service() + end + end + end + +end diff --git a/spec/unit/provider/service/init_service_spec.rb b/spec/unit/provider/service/init_service_spec.rb new file mode 100644 index 0000000000..77b22c8cf4 --- /dev/null +++ b/spec/unit/provider/service/init_service_spec.rb @@ -0,0 +1,212 @@ +# +# Author:: AJ Christensen (<aj@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Init, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => "ps -ef"} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Init.new(@new_resource, @run_context) + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @stdout = StringIO.new(<<-PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb +aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash +aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb +PS + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + end + + it "should create a current resource with the name of the new resource" do + @provider.load_current_resource + @provider.current_resource.should equal(@current_resource) + end + + it "should set the current resources service name to the new resources service name" do + @provider.load_current_resource + @current_resource.service_name.should == 'chef' + end + + describe "when the service supports status" do + before do + @new_resource.supports({:status => true}) + end + + it "should run '/etc/init.d/service_name status'" do + @provider.should_receive(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + end + + it "should set running to true if the the status command returns 0" do + @provider.stub!(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the status command returns anything except 0" do + @status.stub!(:exitstatus).and_return(1) + @provider.stub!(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should set running to false if the status command raises" do + @provider.stub!(:shell_out).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @current_resource.running.should be_false + end + end + + describe "when a status command has been specified" do + before do + @new_resource.stub!(:status_command).and_return("/etc/init.d/chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.should_receive(:shell_out).with("/etc/init.d/chefhasmonkeypants status").and_return(@status) + @provider.load_current_resource + end + + end + + describe "when the node has not specified a ps command" do + + it "should raise an error if the node has a nil ps attribute" do + @node.automatic_attrs[:command] = {:ps => nil} + @provider.load_current_resource + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should raise an error if the node has an empty ps attribute" do + @node.automatic_attrs[:command] = {:ps => ""} + @provider.load_current_resource + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + end + + + describe "when we have a 'ps' attribute" do + it "should shell_out! the node's ps command" do + @provider.should_receive(:shell_out!).and_return(@status) + @provider.load_current_resource + end + + it "should set running to true if the regex matches the output" do + @stdout = StringIO.new(<<-RUNNING_PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 chef +aj 7842 5057 0 21:26 pts/2 00:00:06 poos +RUNNING_PS + @status.stub!(:stdout).and_return(@stdout) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the regex doesn't match" do + @provider.stub!(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should raise an exception if ps fails" do + @provider.stub!(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + + describe "when starting the service" do + it "should call the start command if one is specified" do + @new_resource.start_command("/etc/init.d/chef startyousillysally") + @provider.should_receive(:shell_out!).with("/etc/init.d/chef startyousillysally") + @provider.start_service() + end + + it "should call '/etc/init.d/service_name start' if no start command is specified" do + @provider.should_receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} start") + @provider.start_service() + end + end + + describe Chef::Provider::Service::Init, "stop_service" do + it "should call the stop command if one is specified" do + @new_resource.stop_command("/etc/init.d/chef itoldyoutostop") + @provider.should_receive(:shell_out!).with("/etc/init.d/chef itoldyoutostop") + @provider.stop_service() + end + + it "should call '/etc/init.d/service_name stop' if no stop command is specified" do + @provider.should_receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} stop") + @provider.stop_service() + end + end + + describe "when restarting a service" do + it "should call 'restart' on the service_name if the resource supports it" do + @new_resource.supports({:restart => true}) + @provider.should_receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} restart") + @provider.restart_service() + end + + it "should call the restart_command if one has been specified" do + @new_resource.restart_command("/etc/init.d/chef restartinafire") + @provider.should_receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} restartinafire") + @provider.restart_service() + end + + it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do + @provider.should_receive(:stop_service) + @provider.should_receive(:sleep).with(1) + @provider.should_receive(:start_service) + @provider.restart_service() + end + end + + describe "when reloading a service" do + it "should call 'reload' on the service if it supports it" do + @new_resource.supports({:reload => true}) + @provider.should_receive(:shell_out!).with("/etc/init.d/chef reload") + @provider.reload_service() + end + + it "should should run the user specified reload command if one is specified and the service doesn't support reload" do + @new_resource.reload_command("/etc/init.d/chef lollerpants") + @provider.should_receive(:shell_out!).with("/etc/init.d/chef lollerpants") + @provider.reload_service() + end + end +end diff --git a/spec/unit/provider/service/insserv_service_spec.rb b/spec/unit/provider/service/insserv_service_spec.rb new file mode 100644 index 0000000000..c823d511b5 --- /dev/null +++ b/spec/unit/provider/service/insserv_service_spec.rb @@ -0,0 +1,76 @@ +# +# Author:: Bryan McLellan <btm@loftninjas.org> +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Insserv do + before(:each) do + @node = Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + @node.automatic_attrs[:command] = {:ps => "ps -ax"} + + @new_resource = Chef::Resource::Service.new("initgrediant") + @current_resource = Chef::Resource::Service.new("initgrediant") + + @provider = Chef::Provider::Service::Insserv.new(@new_resource, @run_context) + @status = mock("Process::Status mock", :exitstatus => 0, :stdout => "") + @provider.stub!(:shell_out!).and_return(@status) + end + + describe "load_current_resource" do + describe "when startup links exist" do + before do + Dir.stub!(:glob).with("/etc/rc**/S*initgrediant").and_return(["/etc/rc5.d/S18initgrediant", "/etc/rc2.d/S18initgrediant", "/etc/rc4.d/S18initgrediant", "/etc/rc3.d/S18initgrediant"]) + end + + it "sets the current enabled status to true" do + @provider.load_current_resource + @provider.current_resource.enabled.should be_true + end + end + + describe "when startup links do not exist" do + before do + Dir.stub!(:glob).with("/etc/rc**/S*initgrediant").and_return([]) + end + + it "sets the current enabled status to false" do + @provider.load_current_resource + @provider.current_resource.enabled.should be_false + end + end + + end + + describe "enable_service" do + it "should call insserv and create the default links" do + @provider.should_receive(:run_command).with({:command=>"/sbin/insserv -r -f #{@new_resource.service_name}"}) + @provider.should_receive(:run_command).with({:command=>"/sbin/insserv -d -f #{@new_resource.service_name}"}) + @provider.enable_service + end + end + + describe "disable_service" do + it "should call insserv and remove the links" do + @provider.should_receive(:run_command).with({:command=>"/sbin/insserv -r -f #{@new_resource.service_name}"}) + @provider.disable_service + end + end +end + diff --git a/spec/unit/provider/service/invokercd_service_spec.rb b/spec/unit/provider/service/invokercd_service_spec.rb new file mode 100644 index 0000000000..ace2ad24e3 --- /dev/null +++ b/spec/unit/provider/service/invokercd_service_spec.rb @@ -0,0 +1,212 @@ +# +# Author:: AJ Christensen (<aj@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Invokercd, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => "ps -ef"} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Invokercd.new(@new_resource, @run_context) + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @stdout = StringIO.new(<<-PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb +aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash +aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb +PS + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + end + + it "should create a current resource with the name of the new resource" do + @provider.load_current_resource + @provider.current_resource.should equal(@current_resource) + end + + it "should set the current resources service name to the new resources service name" do + @provider.load_current_resource + @current_resource.service_name.should == 'chef' + end + + describe "when the service supports status" do + before do + @new_resource.supports({:status => true}) + end + + it "should run '/usr/sbin/invoke-rc.d service_name status'" do + @provider.should_receive(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + end + + it "should set running to true if the the status command returns 0" do + @provider.stub!(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the status command returns anything except 0" do + @status.stub!(:exitstatus).and_return(1) + @provider.stub!(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should set running to false if the status command raises" do + @provider.stub!(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.load_current_resource + @current_resource.running.should be_false + end + end + + describe "when a status command has been specified" do + before do + @new_resource.stub!(:status_command).and_return("/usr/sbin/invoke-rc.d chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.should_receive(:shell_out).with("/usr/sbin/invoke-rc.d chefhasmonkeypants status").and_return(@status) + @provider.load_current_resource + end + + end + + describe "when the node has not specified a ps command" do + it "should raise error if the node has a nil ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => nil} + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should raise error if the node has an empty ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => ""} + @provider.action = :start + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + end + + + describe "when we have a 'ps' attribute" do + it "should shell_out! the node's ps command" do + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.should_receive(:shell_out!).with(@node[:command][:ps]).and_return(@status) + @provider.load_current_resource + end + + it "should set running to true if the regex matches the output" do + @stdout = StringIO.new(<<-RUNNING_PS) +aj 7842 5057 0 21:26 pts/2 00:00:06 chef +aj 7842 5057 0 21:26 pts/2 00:00:06 poos +RUNNING_PS + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.should_receive(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the regex doesn't match" do + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.should_receive(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should raise an exception if ps fails" do + @provider.stub!(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.action = :start + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + + describe "when starting the service" do + it "should call the start command if one is specified" do + @new_resource.start_command("/usr/sbin/invoke-rc.d chef startyousillysally") + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef startyousillysally") + @provider.start_service() + end + + it "should call '/usr/sbin/invoke-rc.d service_name start' if no start command is specified" do + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} start") + @provider.start_service() + end + end + + describe Chef::Provider::Service::Invokercd, "stop_service" do + it "should call the stop command if one is specified" do + @new_resource.stop_command("/usr/sbin/invoke-rc.d chef itoldyoutostop") + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef itoldyoutostop") + @provider.stop_service() + end + + it "should call '/usr/sbin/invoke-rc.d service_name stop' if no stop command is specified" do + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} stop") + @provider.stop_service() + end + end + + describe "when restarting a service" do + it "should call 'restart' on the service_name if the resource supports it" do + @new_resource.supports({:restart => true}) + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restart") + @provider.restart_service() + end + + it "should call the restart_command if one has been specified" do + @new_resource.restart_command("/usr/sbin/invoke-rc.d chef restartinafire") + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restartinafire") + @provider.restart_service() + end + + it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do + @provider.should_receive(:stop_service) + @provider.should_receive(:sleep).with(1) + @provider.should_receive(:start_service) + @provider.restart_service() + end + end + + describe "when reloading a service" do + it "should call 'reload' on the service if it supports it" do + @new_resource.supports({:reload => true}) + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef reload") + @provider.reload_service() + end + + it "should should run the user specified reload command if one is specified and the service doesn't support reload" do + @new_resource.reload_command("/usr/sbin/invoke-rc.d chef lollerpants") + @provider.should_receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef lollerpants") + @provider.reload_service() + end + end +end diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb new file mode 100644 index 0000000000..9c3ec340a2 --- /dev/null +++ b/spec/unit/provider/service/macosx_spec.rb @@ -0,0 +1,229 @@ +# +# Author:: Igor Afonov <afonov@gmail.com> +# Copyright:: Copyright (c) 2011 Igor Afonov +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Macosx do + let(:node) { Chef::Node.new } + let(:events) {Chef::EventDispatch::Dispatcher.new} + let(:run_context) { Chef::RunContext.new(node, {}, events) } + let(:provider) { described_class.new(new_resource, run_context) } + let(:stdout) { StringIO.new } + + before do + Dir.stub!(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], []) + provider.stub!(:shell_out!). + with("launchctl list", {:group => 1001, :user => 101}). + and_return(mock("ouput", :stdout => stdout)) + + File.stub!(:stat).and_return(mock("stat", :gid => 1001, :uid => 101)) + end + + ["redis-server", "io.redis.redis-server"].each do |service_name| + context "when service name is given as #{service_name}" do + let(:new_resource) { Chef::Resource::Service.new(service_name) } + let!(:current_resource) { Chef::Resource::Service.new(service_name) } + + describe "#load_current_resource" do + context "when launchctl returns pid in service list" do + let(:stdout) { StringIO.new <<-SVC_LIST } +12761 - 0x100114220.old.machinit.thing +7777 - io.redis.redis-server +- - com.lol.stopped-thing +SVC_LIST + + before do + provider.load_current_resource + end + + it "sets resource running state to true" do + provider.current_resource.running.should be_true + end + + it "sets resouce enabled state to true" do + provider.current_resource.enabled.should be_true + end + end + + describe "running unsupported actions" do + before do + Dir.stub!(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], []) + end + it "should throw an exception when enable action is attempted" do + lambda {provider.run_action(:enable)}.should raise_error(Chef::Exceptions::UnsupportedAction) + end + it "should throw an exception when reload action is attempted" do + lambda {provider.run_action(:reload)}.should raise_error(Chef::Exceptions::UnsupportedAction) + end + it "should throw an exception when disable action is attempted" do + lambda {provider.run_action(:disable)}.should raise_error(Chef::Exceptions::UnsupportedAction) + end + end + context "when launchctl returns empty service pid" do + let(:stdout) { StringIO.new <<-SVC_LIST } +12761 - 0x100114220.old.machinit.thing +- - io.redis.redis-server +- - com.lol.stopped-thing +SVC_LIST + + before do + provider.load_current_resource + end + + it "sets resource running state to false" do + provider.current_resource.running.should be_false + end + + it "sets resouce enabled state to true" do + provider.current_resource.enabled.should be_true + end + end + + context "when launchctl doesn't return service entry at all" do + let(:stdout) { StringIO.new <<-SVC_LIST } +12761 - 0x100114220.old.machinit.thing +- - com.lol.stopped-thing +SVC_LIST + + it "sets service running state to false" do + provider.load_current_resource + provider.current_resource.running.should be_false + end + + context "and plist for service is not available" do + before do + Dir.stub!(:glob).and_return([]) + provider.load_current_resource + end + + it "sets resouce enabled state to false" do + provider.current_resource.enabled.should be_false + end + end + + context "and plist for service is available" do + before do + Dir.stub!(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], []) + provider.load_current_resource + end + + it "sets resouce enabled state to true" do + provider.current_resource.enabled.should be_true + end + end + + describe "and several plists match service name" do + it "throws exception" do + Dir.stub!(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist", + "/Users/wtf/something.plist"]) + provider.load_current_resource + provider.define_resource_requirements + lambda { provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + end + end + describe "#start_service" do + before do + Chef::Resource::Service.stub!(:new).and_return(current_resource) + provider.load_current_resource + current_resource.stub!(:running).and_return(false) + end + + it "calls the start command if one is specified and service is not running" do + new_resource.stub!(:start_command).and_return("cowsay dirty") + + provider.should_receive(:shell_out!).with("cowsay dirty") + provider.start_service + end + + it "shows warning message if service is already running" do + current_resource.stub!(:running).and_return(true) + Chef::Log.should_receive(:debug).with("service[#{service_name}] already running, not starting") + + provider.start_service + end + + it "starts service via launchctl if service found" do + provider.should_receive(:shell_out!). + with("launchctl load -w '/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist'", + :group => 1001, :user => 101). + and_return(0) + + provider.start_service + end + end + + describe "#stop_service" do + before do + Chef::Resource::Service.stub!(:new).and_return(current_resource) + + provider.load_current_resource + current_resource.stub!(:running).and_return(true) + end + + it "calls the stop command if one is specified and service is running" do + new_resource.stub!(:stop_command).and_return("kill -9 123") + + provider.should_receive(:shell_out!).with("kill -9 123") + provider.stop_service + end + + it "shows warning message if service is not running" do + current_resource.stub!(:running).and_return(false) + Chef::Log.should_receive(:debug).with("service[#{service_name}] not running, not stopping") + + provider.stop_service + end + + it "stops the service via launchctl if service found" do + provider.should_receive(:shell_out!). + with("launchctl unload '/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist'", + :group => 1001, :user => 101). + and_return(0) + + provider.stop_service + end + end + + describe "#restart_service" do + before do + Chef::Resource::Service.stub!(:new).and_return(current_resource) + + provider.load_current_resource + current_resource.stub!(:running).and_return(true) + provider.stub!(:sleep) + end + + it "issues a command if given" do + new_resource.stub!(:restart_command).and_return("reload that thing") + + provider.should_receive(:shell_out!).with("reload that thing") + provider.restart_service + end + + it "stops and then starts service" do + provider.should_receive(:stop_service) + provider.should_receive(:start_service); + + provider.restart_service + end + end + end + end +end diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb new file mode 100644 index 0000000000..dd874a4f05 --- /dev/null +++ b/spec/unit/provider/service/redhat_spec.rb @@ -0,0 +1,156 @@ +# +# Author:: AJ Christensen (<aj@hjksolutions.com>) +# Copyright:: Copyright (c) 2008 HJK Solutions, LLC +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper")) +require 'ostruct' + +shared_examples_for "define_resource_requirements_common" do + it "should raise an error if /sbin/chkconfig does not exist" do + File.stub!(:exists?).with("/sbin/chkconfig").and_return(false) + @provider.stub!(:shell_out).with("/sbin/service chef status").and_raise(Errno::ENOENT) + @provider.stub!(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_raise(Errno::ENOENT) + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should not raise an error if the service exists but is not added to any runlevels" do + status = mock("Status", :exitstatus => 0, :stdout => "" , :stderr => "") + @provider.should_receive(:shell_out).with("/sbin/service chef status").and_return(status) + chkconfig = mock("Chkconfig", :exitstatus => 0, :stdout => "", :stderr => "service chef supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add chef')") + @provider.should_receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should_not raise_error + end +end + +describe "Chef::Provider::Service::Redhat" do + + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => 'foo'} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Redhat.new(@new_resource, @run_context) + @provider.action = :start + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + File.stub!(:exists?).with("/sbin/chkconfig").and_return(true) + end + + describe "while not in why run mode" do + before(:each) do + Chef::Config[:why_run] = false + end + + describe "load current resource" do + it "sets the current enabled status to true if the service is enabled for any run level" do + status = mock("Status", :exitstatus => 0, :stdout => "" , :stderr => "") + @provider.should_receive(:shell_out).with("/sbin/service chef status").and_return(status) + chkconfig = mock("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "") + @provider.should_receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) + @provider.instance_variable_get("@service_missing").should be_false + @provider.load_current_resource + @current_resource.enabled.should be_true + end + + it "sets the current enabled status to false if the regex does not match" do + status = mock("Status", :exitstatus => 0, :stdout => "" , :stderr => "") + @provider.should_receive(:shell_out).with("/sbin/service chef status").and_return(status) + chkconfig = mock("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:off 6:off", :stderr => "") + @provider.should_receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) + @provider.instance_variable_get("@service_missing").should be_false + @provider.load_current_resource.should eql(@current_resource) + @current_resource.enabled.should be_false + end + end + + describe "define resource requirements" do + it_should_behave_like "define_resource_requirements_common" + + context "when the service does not exist" do + before do + status = mock("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service") + @provider.should_receive(:shell_out).with("/sbin/service chef status").and_return(status) + chkconfig = mock("Chkconfig", :existatus=> 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory") + @provider.should_receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) + @provider.load_current_resource + @provider.define_resource_requirements + end + + [ "start", "reload", "restart", "enable" ].each do |action| + it "should raise an error when the action is #{action}" do + @provider.action = action + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + [ "stop", "disable" ].each do |action| + it "should not raise an error when the action is #{action}" do + @provider.action = action + lambda { @provider.process_resource_requirements }.should_not raise_error + end + end + end + end + end + + describe "while in why run mode" do + before(:each) do + Chef::Config[:why_run] = true + end + + after do + Chef::Config[:why_run] = false + end + + describe "define resource requirements" do + it_should_behave_like "define_resource_requirements_common" + + it "should not raise an error if the service does not exist" do + status = mock("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service") + @provider.should_receive(:shell_out).with("/sbin/service chef status").and_return(status) + chkconfig = mock("Chkconfig", :existatus=> 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory") + @provider.should_receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should_not raise_error + end + end + end + + describe "enable_service" do + it "should call chkconfig to add 'service_name'" do + @provider.should_receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} on") + @provider.enable_service + end + end + + describe "disable_service" do + it "should call chkconfig to del 'service_name'" do + @provider.should_receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} off") + @provider.disable_service + end + end + +end diff --git a/spec/unit/provider/service/simple_service_spec.rb b/spec/unit/provider/service/simple_service_spec.rb new file mode 100644 index 0000000000..cc0173e246 --- /dev/null +++ b/spec/unit/provider/service/simple_service_spec.rb @@ -0,0 +1,171 @@ +# +# Author:: Mathieu Sauve-Frankel <msf@kisoku.net> +# Copyright:: Copyright (c) 2009, Mathieu Sauve Frankel +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Simple, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @node.automatic_attrs[:command] = {:ps => "ps -ef"} + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("chef") + @current_resource = Chef::Resource::Service.new("chef") + + @provider = Chef::Provider::Service::Simple.new(@new_resource, @run_context) + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ) +aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb +aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash +aj 8119 6041 0 21:34 pts/3 00:00:03 vi simple_service_spec.rb +NOMOCKINGSTRINGSPLZ + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + end + + it "should create a current resource with the name of the new resource" do + Chef::Resource::Service.should_receive(:new).and_return(@current_resource) + @provider.load_current_resource + end + + it "should set the current resources service name to the new resources service name" do + @current_resource.should_receive(:service_name).with(@new_resource.service_name) + @provider.load_current_resource + end + + it "should raise error if the node has a nil ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => nil} + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should raise error if the node has an empty ps attribute and no other means to get status" do + @node.automatic_attrs[:command] = {:ps => ""} + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + describe "when we have a 'ps' attribute" do + it "should shell_out! the node's ps command" do + @provider.should_receive(:shell_out!).with(@node[:command][:ps]).and_return(@status) + @provider.load_current_resource + end + + it "should read stdout of the ps command" do + @provider.stub!(:shell_out!).and_return(@status) + @stdout.should_receive(:each_line).and_return(true) + @provider.load_current_resource + end + + it "should set running to true if the regex matches the output" do + @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ) +aj 7842 5057 0 21:26 pts/2 00:00:06 chef +aj 7842 5057 0 21:26 pts/2 00:00:06 poos +NOMOCKINGSTRINGSPLZ + @status = mock("Status", :exitstatus => 0, :stdout => @stdout) + @provider.stub!(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the regex doesn't match" do + @provider.stub!(:shell_out!).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + + it "should raise an exception if ps fails" do + @provider.stub!(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) + @provider.action = :start + @provider.load_current_resource + @provider.define_resource_requirements + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + + + + describe "when starting the service" do + it "should call the start command if one is specified" do + @new_resource.stub!(:start_command).and_return("#{@new_resource.start_command}") + @provider.should_receive(:shell_out!).with("#{@new_resource.start_command}") + @provider.start_service() + end + + it "should raise an exception if no start command is specified" do + @provider.define_resource_requirements + @provider.action = :start + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + describe "when stopping a service" do + it "should call the stop command if one is specified" do + @new_resource.stop_command("/etc/init.d/themadness stop") + @provider.should_receive(:shell_out!).with("/etc/init.d/themadness stop") + @provider.stop_service() + end + + it "should raise an exception if no stop command is specified" do + @provider.define_resource_requirements + @provider.action = :stop + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + end + + describe Chef::Provider::Service::Simple, "restart_service" do + it "should call the restart command if one has been specified" do + @new_resource.restart_command("/etc/init.d/foo restart") + @provider.should_receive(:shell_out!).with("/etc/init.d/foo restart") + @provider.restart_service() + end + + it "should raise an exception if the resource doesn't support restart, no restart command is provided, and no stop command is provided" do + @provider.define_resource_requirements + @provider.action = :restart + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::Service) + end + + it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do + @provider.should_receive(:stop_service) + @provider.should_receive(:sleep).with(1) + @provider.should_receive(:start_service) + @provider.restart_service() + end + end + + describe Chef::Provider::Service::Simple, "reload_service" do + it "should raise an exception if reload is requested but no command is specified" do + @provider.define_resource_requirements + @provider.action = :reload + lambda { @provider.process_resource_requirements }.should raise_error(Chef::Exceptions::UnsupportedAction) + end + + it "should should run the user specified reload command if one is specified" do + @new_resource.reload_command("kill -9 1") + @provider.should_receive(:shell_out!).with("kill -9 1") + @provider.reload_service() + end + end +end diff --git a/spec/unit/provider/service/solaris_smf_service_spec.rb b/spec/unit/provider/service/solaris_smf_service_spec.rb new file mode 100644 index 0000000000..3ea2902755 --- /dev/null +++ b/spec/unit/provider/service/solaris_smf_service_spec.rb @@ -0,0 +1,140 @@ +# +# Author:: Toomas Pelberg (<toomasp@gmx.net>) +# Copyright:: Copyright (c) 2010 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Solaris do + before(:each) do + @node =Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new('chef') + + @current_resource = Chef::Resource::Service.new('chef') + + @provider = Chef::Provider::Service::Solaris.new(@new_resource, @run_context) + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @stdin = StringIO.new + @stdout = StringIO.new + @stderr = StringIO.new + @pid = 2342 + @stdout_string = "state disabled" + @stdout.stub!(:gets).and_return(@stdout_string) + end + + it "should raise an error if /bin/svcs does not exist" do + File.should_receive(:exists?).with("/bin/svcs").and_return(false) + lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Service) + end + + describe "on a host with /bin/svcs" do + + before do + File.stub!(:exists?).with('/bin/svcs').and_return(true) + end + + describe "when discovering the current service state" do + it "should create a current resource with the name of the new resource" do + @provider.stub!(:popen4).with("/bin/svcs -l chef").and_return(@status) + Chef::Resource::Service.should_receive(:new).and_return(@current_resource) + @provider.load_current_resource + end + + + it "should return the current resource" do + @provider.stub!(:popen4).with("/bin/svcs -l chef").and_return(@status) + @provider.load_current_resource.should eql(@current_resource) + end + + it "should popen4 '/bin/svcs -l service_name'" do + @provider.should_receive(:popen4).with("/bin/svcs -l chef").and_return(@status) + @provider.load_current_resource + end + + it "should mark service as not running" do + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + it "should mark service as running" do + @stdout.stub!(:each).and_yield("state online") + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @current_resource.should_receive(:running).with(true) + @provider.load_current_resource + end + end + + describe "when enabling the service" do + before(:each) do + #@provider = Chef::Provider::Service::Solaris.new(@node, @new_resource) + @provider.current_resource = @current_resource + @current_resource.enabled(true) + end + + it "should call svcadm enable chef" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/svcadm enable chef"}) + @provider.should_receive(:service_status).and_return(@current_resource) + @provider.enable_service.should be_true + end + + it "should call svcadm enable chef for start_service" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/svcadm enable chef"}) + @provider.should_receive(:service_status).and_return(@current_resource) + @provider.start_service.should be_true + end + + end + + + describe "when disabling the service" do + before(:each) do + @provider.current_resource = @current_resource + @current_resource.enabled(false) + end + + it "should call svcadm disable chef" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/svcadm disable chef"}) + @provider.should_receive(:service_status).and_return(@current_resource) + @provider.disable_service.should be_false + end + + it "should call svcadm disable chef for stop_service" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/svcadm disable chef"}) + @provider.should_receive(:service_status).and_return(@current_resource) + @provider.stop_service.should be_false + end + + end + + describe "when reloading the service" do + before(:each) do + @status = mock("Process::Status", :exitstatus => 0) + @provider.current_resource = @current_resource + end + + it "should call svcadm refresh chef" do + @provider.should_receive(:run_command).with({:command => "/usr/sbin/svcadm refresh chef"}).and_return(@status) + @provider.reload_service.should be_true + end + + end + end +end diff --git a/spec/unit/provider/service/systemd_service_spec.rb b/spec/unit/provider/service/systemd_service_spec.rb new file mode 100644 index 0000000000..dbdedecd40 --- /dev/null +++ b/spec/unit/provider/service/systemd_service_spec.rb @@ -0,0 +1,239 @@ +# +# Author:: Stephen Haynes (<sh@nomitor.com>) +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Systemd do + before(:each) do + @node = Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + @new_resource = Chef::Resource::Service.new('rsyslog.service') + @provider = Chef::Provider::Service::Systemd.new(@new_resource, @run_context) + end + + describe "load_current_resource" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog.service') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @provider.stub!(:is_active?).and_return(false) + @provider.stub!(:is_enabled?).and_return(false) + end + + it "should create a current resource with the name of the new resource" do + Chef::Resource::Service.should_receive(:new).and_return(@current_resource) + @provider.load_current_resource + end + + it "should set the current resources service name to the new resources service name" do + @current_resource.should_receive(:service_name).with(@new_resource.service_name) + @provider.load_current_resource + end + + it "should check if the service is running" do + @provider.should_receive(:is_active?) + @provider.load_current_resource + end + + it "should set running to true if the service is running" do + @provider.stub!(:is_active?).and_return(true) + @current_resource.should_receive(:running).with(true) + @provider.load_current_resource + end + + it "should set running to false if the service is not running" do + @provider.stub!(:is_active?).and_return(false) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + describe "when a status command has been specified" do + before do + @new_resource.stub!(:status_command).and_return("/bin/chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.stub!(:run_command_with_systems_locale).with({:command => "/bin/chefhasmonkeypants status"}).and_return(0) + @current_resource.should_receive(:running).with(true) + @provider.load_current_resource + end + + it "should run the services status command if one has been specified and properly set status check state" do + @provider.stub!(:run_command_with_systems_locale).with({:command => "/bin/chefhasmonkeypants status"}).and_return(0) + @provider.load_current_resource + @provider.instance_variable_get("@status_check_success").should be_true + end + + it "should set running to false if it catches a Chef::Exceptions::Exec when using a status command" do + @provider.stub!(:run_command_with_systems_locale).and_raise(Chef::Exceptions::Exec) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + it "should update state to indicate status check failed when an exception is thrown using a status command" do + @provider.stub!(:run_command_with_systems_locale).and_raise(Chef::Exceptions::Exec) + @provider.load_current_resource + @provider.instance_variable_get("@status_check_success").should be_false + end + end + + it "should check if the service is enabled" do + @provider.should_receive(:is_enabled?) + @provider.load_current_resource + end + + it "should set enabled to true if the service is enabled" do + @provider.stub!(:is_enabled?).and_return(true) + @current_resource.should_receive(:enabled).with(true) + @provider.load_current_resource + end + + it "should set enabled to false if the service is not enabled" do + @provider.stub!(:is_enabled?).and_return(false) + @current_resource.should_receive(:enabled).with(false) + @provider.load_current_resource + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + end + + describe "start and stop service" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog.service') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.current_resource = @current_resource + end + + it "should call the start command if one is specified" do + @new_resource.stub!(:start_command).and_return("/sbin/rsyslog startyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog startyousillysally") + @provider.start_service + end + + it "should call '/bin/systemctl start service_name' if no start command is specified" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl start #{@new_resource.service_name}"}).and_return(0) + @provider.start_service + end + + it "should not call '/bin/systemctl start service_name' if it is already running" do + @current_resource.stub!(:running).and_return(true) + @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl start #{@new_resource.service_name}"}).and_return(0) + @provider.start_service + end + + it "should call the restart command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:restart_command).and_return("/sbin/rsyslog restartyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog restartyousillysally") + @provider.restart_service + end + + it "should call '/bin/systemctl restart service_name' if no restart command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl restart #{@new_resource.service_name}"}).and_return(0) + @provider.restart_service + end + + it "should call the reload command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog reloadyousillysally") + @provider.reload_service + end + + it "should call '/bin/systemctl reload service_name' if no reload command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl reload #{@new_resource.service_name}"}).and_return(0) + @provider.reload_service + end + + it "should call the stop command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:stop_command).and_return("/sbin/rsyslog stopyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog stopyousillysally") + @provider.stop_service + end + + it "should call '/bin/systemctl stop service_name' if no stop command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl stop #{@new_resource.service_name}"}).and_return(0) + @provider.stop_service + end + + it "should not call '/bin/systemctl stop service_name' if it is already stopped" do + @current_resource.stub!(:running).and_return(false) + @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl stop #{@new_resource.service_name}"}).and_return(0) + @provider.stop_service + end + end + + describe "enable and disable service" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog.service') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.current_resource = @current_resource + end + + it "should call '/bin/systemctl enable service_name' to enable the service" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl enable #{@new_resource.service_name}"}).and_return(0) + @provider.enable_service + end + + it "should call '/bin/systemctl disable service_name' to disable the service" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl disable #{@new_resource.service_name}"}).and_return(0) + @provider.disable_service + end + end + + describe "is_active?" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog.service') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + end + + it "should return true if '/bin/systemctl is-active service_name' returns 0" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-active rsyslog.service', :ignore_failure => true}).and_return(0) + @provider.is_active?.should be_true + end + + it "should return false if '/bin/systemctl is-active service_name' returns anything except 0" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-active rsyslog.service', :ignore_failure => true}).and_return(1) + @provider.is_active?.should be_false + end + end + + describe "is_enabled?" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog.service') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + end + + it "should return true if '/bin/systemctl is-enabled service_name' returns 0" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-enabled rsyslog.service', :ignore_failure => true}).and_return(0) + @provider.is_enabled?.should be_true + end + + it "should return false if '/bin/systemctl is-enabled service_name' returns anything except 0" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-enabled rsyslog.service', :ignore_failure => true}).and_return(1) + @provider.is_enabled?.should be_false + end + end +end diff --git a/spec/unit/provider/service/upstart_service_spec.rb b/spec/unit/provider/service/upstart_service_spec.rb new file mode 100644 index 0000000000..2fc49c7aa2 --- /dev/null +++ b/spec/unit/provider/service/upstart_service_spec.rb @@ -0,0 +1,314 @@ +# +# Author:: Bryan McLellan (btm@loftninjas.org) +# Copyright:: Copyright (c) 2010 Bryan McLellan +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Upstart do + before(:each) do + @node =Chef::Node.new + @node.name('upstarter') + @node.automatic_attrs[:platform] = 'ubuntu' + @node.automatic_attrs[:platform_version] = '9.10' + + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Service.new("rsyslog") + @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) + end + + describe "when first created" do + before do + @platform = nil + end + + it "should return /etc/event.d as the upstart job directory when running on Ubuntu 9.04" do + @node.automatic_attrs[:platform_version] = '9.04' + #Chef::Platform.stub!(:find_platform_and_version).and_return([ "ubuntu", "9.04" ]) + @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) + @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/event.d" + @provider.instance_variable_get(:@upstart_conf_suffix).should == "" + end + + it "should return /etc/init as the upstart job directory when running on Ubuntu 9.10" do + @node.automatic_attrs[:platform_version] = '9.10' + @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) + @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/init" + @provider.instance_variable_get(:@upstart_conf_suffix).should == ".conf" + end + + it "should return /etc/init as the upstart job directory by default" do + @node.automatic_attrs[:platform_version] = '9000' + @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) + @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/init" + @provider.instance_variable_get(:@upstart_conf_suffix).should == ".conf" + end + end + + describe "load_current_resource" do + before(:each) do + @node.automatic_attrs[:command] = {:ps => "ps -ax"} + + @current_resource = Chef::Resource::Service.new("rsyslog") + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + + @status = mock("Status", :exitstatus => 0) + @provider.stub!(:popen4).and_return(@status) + @stdin = StringIO.new + @stdout = StringIO.new + @stderr = StringIO.new + @pid = mock("PID") + + ::File.stub!(:exists?).and_return(true) + ::File.stub!(:open).and_return(true) + end + + it "should create a current resource with the name of the new resource" do + Chef::Resource::Service.should_receive(:new).and_return(@current_resource) + @provider.load_current_resource + end + + it "should set the current resources service name to the new resources service name" do + @current_resource.should_receive(:service_name).with(@new_resource.service_name) + @provider.load_current_resource + end + + it "should run '/sbin/status rsyslog'" do + @provider.should_receive(:popen4).with("/sbin/status rsyslog").and_return(@status) + @provider.load_current_resource + end + + describe "when the status command uses the new format" do + before do + end + + it "should set running to true if the the status command returns 0" do + @stdout = StringIO.new("rsyslog start/running") + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the status command returns anything except 0" do + @stdout = StringIO.new("rsyslog stop/waiting") + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + end + + describe "when the status command uses the old format" do + it "should set running to true if the the status command returns 0" do + @stdout = StringIO.new("rsyslog (start) running, process 32225") + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_true + end + + it "should set running to false if the status command returns anything except 0" do + @stdout = StringIO.new("rsyslog (stop) waiting") + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + @provider.load_current_resource + @current_resource.running.should be_false + end + end + + it "should set running to false if it catches a Chef::Exceptions::Exec" do + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_raise(Chef::Exceptions::Exec) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + it "should set enabled to true when it finds 'starts on'" do + @lines = mock("start on filesystem", :gets => "start on filesystem") + ::File.stub!(:open).and_yield(@lines) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + it "should set enabled to false when it finds '#starts on'" do + @lines = mock("start on filesystem", :gets => "#start on filesystem") + ::File.stub!(:open).and_yield(@lines) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + it "should assume disable when no job configuration file is found" do + ::File.stub!(:exists?).and_return(false) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + + + it "should track state when the upstart configuration file fails to load" do + File.should_receive(:exists?).and_return false + @provider.load_current_resource + @provider.instance_variable_get("@config_file_found").should == false + end + + describe "when a status command has been specified" do + before do + @new_resource.stub!(:status_command).and_return("/bin/chefhasmonkeypants status") + end + + it "should run the services status command if one has been specified" do + @provider.stub!(:run_command_with_systems_locale).with({:command => "/bin/chefhasmonkeypants status"}).and_return(0) + @current_resource.should_receive(:running).with(true) + @provider.load_current_resource + end + + it "should track state when the user-provided status command fails" do + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_raise(Chef::Exceptions::Exec) + @provider.load_current_resource + @provider.instance_variable_get("@command_success").should == false + end + + it "should set running to false if it catches a Chef::Exceptions::Exec when using a status command" do + @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_raise(Chef::Exceptions::Exec) + @current_resource.should_receive(:running).with(false) + @provider.load_current_resource + end + end + + it "should track state when we fail to obtain service status via upstart_state" do + @provider.should_receive(:upstart_state).and_raise Chef::Exceptions::Exec + @provider.load_current_resource + @provider.instance_variable_get("@command_success").should == false + end + + it "should return the current resource" do + @provider.load_current_resource.should eql(@current_resource) + end + + + end + + describe "enable and disable service" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog') + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.current_resource = @current_resource + Chef::Util::FileEdit.stub!(:new) + end + + it "should enable the service if it is not enabled" do + @file = Object.new + Chef::Util::FileEdit.stub!(:new).and_return(@file) + @current_resource.stub!(:enabled).and_return(false) + @file.should_receive(:search_file_replace) + @file.should_receive(:write_file) + @provider.enable_service() + end + + it "should disable the service if it is enabled" do + @file = Object.new + Chef::Util::FileEdit.stub!(:new).and_return(@file) + @current_resource.stub!(:enabled).and_return(true) + @file.should_receive(:search_file_replace) + @file.should_receive(:write_file) + @provider.disable_service() + end + + end + + describe "start and stop service" do + before(:each) do + @current_resource = Chef::Resource::Service.new('rsyslog') + + Chef::Resource::Service.stub!(:new).and_return(@current_resource) + @provider.current_resource = @current_resource + end + + it "should call the start command if one is specified" do + @new_resource.stub!(:start_command).and_return("/sbin/rsyslog startyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog startyousillysally") + @provider.start_service() + end + + it "should call '/sbin/start service_name' if no start command is specified" do + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/start #{@new_resource.service_name}"}).and_return(0) + @provider.start_service() + end + + it "should not call '/sbin/start service_name' if it is already running" do + @current_resource.stub!(:running).and_return(true) + @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/sbin/start #{@new_resource.service_name}"}).and_return(0) + @provider.start_service() + end + + it "should pass parameters to the start command if they are provided" do + @new_resource = Chef::Resource::Service.new("rsyslog") + @new_resource.parameters({ "OSD_ID" => "2" }) + @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) + @provider.current_resource = @current_resource + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/start rsyslog OSD_ID=2"}).and_return(0) + @provider.start_service() + end + + it "should call the restart command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:restart_command).and_return("/sbin/rsyslog restartyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog restartyousillysally") + @provider.restart_service() + end + + it "should call '/sbin/restart service_name' if no restart command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/restart #{@new_resource.service_name}"}).and_return(0) + @provider.restart_service() + end + + it "should call '/sbin/start service_name' if restart_service is called for a stopped service" do + @current_resource.stub!(:running).and_return(false) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/start #{@new_resource.service_name}"}).and_return(0) + @provider.restart_service() + end + + it "should call the reload command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog reloadyousillysally") + @provider.reload_service() + end + + it "should call '/sbin/reload service_name' if no reload command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/reload #{@new_resource.service_name}"}).and_return(0) + @provider.reload_service() + end + + it "should call the stop command if one is specified" do + @current_resource.stub!(:running).and_return(true) + @new_resource.stub!(:stop_command).and_return("/sbin/rsyslog stopyousillysally") + @provider.should_receive(:shell_out!).with("/sbin/rsyslog stopyousillysally") + @provider.stop_service() + end + + it "should call '/sbin/stop service_name' if no stop command is specified" do + @current_resource.stub!(:running).and_return(true) + @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/stop #{@new_resource.service_name}"}).and_return(0) + @provider.stop_service() + end + + it "should not call '/sbin/stop service_name' if it is already stopped" do + @current_resource.stub!(:running).and_return(false) + @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/sbin/stop #{@new_resource.service_name}"}).and_return(0) + @provider.stop_service() + end + end +end diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb new file mode 100644 index 0000000000..a68e798d36 --- /dev/null +++ b/spec/unit/provider/service/windows_spec.rb @@ -0,0 +1,235 @@ +# +# Author:: Nuo Yan <nuo@opscode.com> +# Author:: Seth Chisamore <schisamo@opscode.com> +# Copyright:: Copyright (c) 2010-2011 Opscode, Inc +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Provider::Service::Windows, "load_current_resource" do + before(:each) do + @node = Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + @new_resource = Chef::Resource::Service.new("chef") + @provider = Chef::Provider::Service::Windows.new(@new_resource, @run_context) + Object.send(:remove_const, 'Win32') if defined?(Win32) + Win32 = Module.new + Win32::Service = Class.new + Win32::Service::AUTO_START = 0x00000002 + Win32::Service::DISABLED = 0x00000004 + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "running")) + Win32::Service.stub!(:config_info).with(@new_resource.service_name).and_return( + mock("ConfigStruct", :start_type => "auto start")) + Win32::Service.stub!(:exists?).and_return(true) + end + + it "should set the current resources service name to the new resources service name" do + @provider.load_current_resource + @provider.current_resource.service_name.should == 'chef' + end + + it "should return the current resource" do + @provider.load_current_resource.should equal(@provider.current_resource) + end + + it "should set the current resources status" do + @provider.load_current_resource + @provider.current_resource.running.should be_true + end + + it "should set the current resources start type" do + @provider.load_current_resource + @provider.current_resource.enabled.should be_true + end + + describe Chef::Provider::Service::Windows, "start_service" do + before(:each) do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "stopped"), + mock("StatusStruct", :current_state => "running")) + end + + it "should call the start command if one is specified" do + @new_resource.start_command "sc start chef" + @provider.should_receive(:shell_out!).with("#{@new_resource.start_command}").and_return("Starting custom service") + @provider.start_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should use the built-in command if no start command is specified" do + Win32::Service.should_receive(:start).with(@new_resource.service_name) + @provider.start_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should do nothing if the service does not exist" do + Win32::Service.stub!(:exists?).with(@new_resource.service_name).and_return(false) + Win32::Service.should_not_receive(:start).with(@new_resource.service_name) + @provider.start_service + @new_resource.updated_by_last_action?.should be_false + end + + it "should do nothing if the service is running" do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "running")) + @provider.load_current_resource + Win32::Service.should_not_receive(:start).with(@new_resource.service_name) + @provider.start_service + @new_resource.updated_by_last_action?.should be_false + end + end + + describe Chef::Provider::Service::Windows, "stop_service" do + + before(:each) do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "running"), + mock("StatusStruct", :current_state => "stopped")) + end + + it "should call the stop command if one is specified" do + @new_resource.stop_command "sc stop chef" + @provider.should_receive(:shell_out!).with("#{@new_resource.stop_command}").and_return("Stopping custom service") + @provider.stop_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should use the built-in command if no stop command is specified" do + Win32::Service.should_receive(:stop).with(@new_resource.service_name) + @provider.stop_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should do nothing if the service does not exist" do + Win32::Service.stub!(:exists?).with(@new_resource.service_name).and_return(false) + Win32::Service.should_not_receive(:stop).with(@new_resource.service_name) + @provider.stop_service + @new_resource.updated_by_last_action?.should be_false + end + + it "should do nothing if the service is stopped" do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "stopped")) + @provider.load_current_resource + Win32::Service.should_not_receive(:stop).with(@new_resource.service_name) + @provider.stop_service + @new_resource.updated_by_last_action?.should be_false + end + end + + describe Chef::Provider::Service::Windows, "restart_service" do + + it "should call the restart command if one is specified" do + @new_resource.restart_command "sc restart" + @provider.should_receive(:shell_out!).with("#{@new_resource.restart_command}") + @provider.restart_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should stop then start the service if it is running" do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "running"), + mock("StatusStruct", :current_state => "stopped"), + mock("StatusStruct", :current_state => "stopped"), + mock("StatusStruct", :current_state => "running")) + Win32::Service.should_receive(:stop).with(@new_resource.service_name) + Win32::Service.should_receive(:start).with(@new_resource.service_name) + @provider.restart_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should just start the service if it is stopped" do + Win32::Service.stub!(:status).with(@new_resource.service_name).and_return( + mock("StatusStruct", :current_state => "stopped"), + mock("StatusStruct", :current_state => "stopped"), + mock("StatusStruct", :current_state => "running")) + Win32::Service.should_receive(:start).with(@new_resource.service_name) + @provider.restart_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should do nothing if the service does not exist" do + Win32::Service.stub!(:exists?).with(@new_resource.service_name).and_return(false) + Win32::Service.should_not_receive(:stop).with(@new_resource.service_name) + Win32::Service.should_not_receive(:start).with(@new_resource.service_name) + @provider.restart_service + @new_resource.updated_by_last_action?.should be_false + end + + end + + describe Chef::Provider::Service::Windows, "enable_service" do + + before(:each) do + Win32::Service.stub!(:config_info).with(@new_resource.service_name).and_return( + mock("ConfigStruct", :start_type => "disabled")) + end + + it "should enable service" do + Win32::Service.should_receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::AUTO_START) + @provider.enable_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should do nothing if the service does not exist" do + Win32::Service.stub!(:exists?).with(@new_resource.service_name).and_return(false) + Win32::Service.should_not_receive(:configure) + @provider.enable_service + @new_resource.updated_by_last_action?.should be_false + end + + it "should do nothing if the service is enabled" do + Win32::Service.stub!(:config_info).with(@new_resource.service_name).and_return( + mock("ConfigStruct", :start_type => "auto start")) + Win32::Service.should_not_receive(:configure) + @provider.enable_service + @new_resource.updated_by_last_action?.should be_false + end + end + + describe Chef::Provider::Service::Windows, "disable_service" do + + before(:each) do + Win32::Service.stub!(:config_info).with(@new_resource.service_name).and_return( + mock("ConfigStruct", :start_type => "auto start")) + end + + it "should disable service" do + Win32::Service.should_receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::DISABLED) + @provider.disable_service + @new_resource.updated_by_last_action?.should be_true + end + + it "should do nothing if the service does not exist" do + Win32::Service.stub!(:exists?).with(@new_resource.service_name).and_return(false) + Win32::Service.should_not_receive(:configure) + @provider.disable_service + @new_resource.updated_by_last_action?.should be_false + end + + it "should do nothing if the service is disabled" do + Win32::Service.stub!(:config_info).with(@new_resource.service_name).and_return( + mock("ConfigStruct", :start_type => "disabled")) + @provider.load_current_resource + Win32::Service.should_not_receive(:configure) + @provider.disable_service + @new_resource.updated_by_last_action?.should be_false + end + + end +end |