diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index 928d15e69..7892bec61 100755 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -1,83 +1,87 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do desc "Ubuntu service management with `upstart`. This provider manages `upstart` jobs, which have replaced `initd` services on Ubuntu. For `upstart` documentation, see . " # confine to :ubuntu for now because I haven't tested on other platforms confine :operatingsystem => :ubuntu #[:ubuntu, :fedora, :debian] defaultfor :operatingsystem => :ubuntu commands :start => "/sbin/start", :stop => "/sbin/stop", :restart => "/sbin/restart", :status_exec => "/sbin/status", :initctl => "/sbin/initctl" # upstart developer haven't implemented initctl enable/disable yet: # http://www.linuxplanet.com/linuxplanet/tutorials/7033/2/ # has_feature :enableable def self.instances instances = [] execpipe("#{command(:initctl)} list") { |process| process.each_line { |line| # needs special handling of services such as network-interface: # initctl list: # network-interface (lo) start/running # network-interface (eth0) start/running # network-interface-security start/running name = \ if matcher = line.match(/^(network-interface)\s\(([^\)]+)\)/) "#{matcher[1]} INTERFACE=#{matcher[2]}" else line.split.first end instances << new(:name => name) } } instances end def startcmd - if is_upstart? - [command(:start), @resource[:name]] - else - super - end + is_upstart? ? [command(:start), @resource[:name]] : super end def stopcmd - if is_upstart? then - [command(:stop), @resource[:name]] - else - super - end + is_upstart? ? [command(:stop), @resource[:name]] : super end def restartcmd - if is_upstart? then - (@resource[:hasrestart] == :true) && [command(:restart), @resource[:name]] + is_upstart? ? (@resource[:hasrestart] == :true) && [command(:restart), @resource[:name]] : super + end + + def statuscmd + is_upstart? ? nil : super #this is because upstart is broken with its return codes + end + + def status + if @resource[:status] + is_upstart?(@resource[:status]) ? upstart_status(@resource[:status]) : normal_status + elsif is_upstart? + upstart_status else super end end - def statuscmd - if @resource[:hasstatus] == :true then - # Workaround the fact that initctl status command doesn't return - # proper exit codes. Can be removed once LP: #552786 is fixed. - if is_upstart? then - ['sh', '-c', "LANG=C invoke-rc.d #{File::basename(initscript)} status | grep -q '^#{File::basename(initscript)}.*running'" ] - else - super - end + def normal_status + ucommand(:status, false) + ($?.exitstatus == 0) ? :running : :stopped + end + + def upstart_status(exec = @resource[:name]) + output = status_exec(@resource[:name].split) + if (! $?.nil?) && (output =~ /start\//) + return :running + else + return :stopped end end - def is_upstart? - File.symlink?(initscript) && File.readlink(initscript) == "/lib/init/upstart-job" + def is_upstart?(script = initscript) + File.symlink?(script) && File.readlink(script) == "/lib/init/upstart-job" end end diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index 259eb2ce4..27983f6e1 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -1,52 +1,90 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:upstart) describe provider_class do describe "#instances" do it "should be able to find all instances" do processes = ["rc stop/waiting", "ssh start/running, process 712"].join("\n") provider_class.stubs(:execpipe).yields(processes) provider_class.instances.map {|provider| provider.name}.should =~ ["rc","ssh"] end it "should attach the interface name for network interfaces" do processes = ["network-interface (eth0)"].join("\n") provider_class.stubs(:execpipe).yields(processes) provider_class.instances.first.name.should == "network-interface INTERFACE=eth0" end end describe "#status" do it "should allow the user to override the status command" do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart, :status => "/bin/foo") provider = provider_class.new(resource) # Because we stub execution, we also need to stub the result of it, or a # previously failing command execution will cause this test to do the # wrong thing. provider.expects(:ucommand) $?.stubs(:exitstatus).returns(0) provider.status.should == :running end it "should use the default status command if none is specified" do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) provider = provider_class.new(resource) + provider.stubs(:is_upstart?).returns(true) provider.expects(:status_exec).with(["foo"]).returns("foo start/running, process 1000") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :running end it "should properly handle services with 'start' in their name" do resource = Puppet::Type.type(:service).new(:name => "foostartbar", :provider => :upstart) provider = provider_class.new(resource) + provider.stubs(:is_upstart?).returns(true) provider.expects(:status_exec).with(["foostartbar"]).returns("foostartbar stop/waiting") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :stopped + end + end + describe "inheritance" do + let :resource do + resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) + end + + let :provider do + provider = provider_class.new(resource) end + + describe "when upstart job" do + before(:each) do + provider.stubs(:is_upstart?).returns(true) + end + ["start", "stop"].each do |command| + it "should return the #{command}cmd of its parent provider" do + provider.send("#{command}cmd".to_sym).should == [provider.command(command.to_sym), resource.name] + end + end + it "should return nil for the statuscmd" do + provider.statuscmd.should be_nil + end + end + + describe "when init script" do + before(:each) do + provider.stubs(:is_upstart?).returns(false) + end + ["start", "stop", "status"].each do |command| + it "should return the #{command}cmd of its parent provider" do + provider.expects(:search).with('foo').returns("/etc/init.d/foo") + provider.send("#{command}cmd".to_sym).should == ["/etc/init.d/foo", command.to_sym] + end + end + end + end end