diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index 902cb18c6..b7e2498d3 100755 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -1,134 +1,140 @@ # The standard init-based service type. Many other service types are # customizations of this module. Puppet::Type.type(:service).provide :init, :parent => :base do desc "Standard `init`-style service management." def self.defpath case Facter.value(:operatingsystem) when "FreeBSD" ["/etc/rc.d", "/usr/local/etc/rc.d"] when "HP-UX" "/sbin/init.d" when "Archlinux" "/etc/rc.d" else "/etc/init.d" end end # We can't confine this here, because the init path can be overridden. #confine :exists => defpath # List all services of this type. def self.instances get_services(self.defpath) end def self.get_services(defpath, exclude=[]) defpath = [defpath] unless defpath.is_a? Array instances = [] defpath.each do |path| unless FileTest.directory?(path) Puppet.debug "Service path #{path} does not exist" next end check = [:ensure] check << :enable if public_method_defined? :enabled? Dir.entries(path).each do |name| fullpath = File.join(path, name) next if name =~ /^\./ next if exclude.include? name next if not FileTest.executable?(fullpath) + next if not is_init?(fullpath) instances << new(:name => name, :path => path, :hasstatus => true) end end instances end # Mark that our init script supports 'status' commands. def hasstatus=(value) case value when true, "true"; @parameters[:hasstatus] = true when false, "false"; @parameters[:hasstatus] = false else raise Puppet::Error, "Invalid 'hasstatus' value #{value.inspect}" end end # Where is our init script? def initscript @initscript ||= self.search(@resource[:name]) end def paths @paths ||= @resource[:path].find_all do |path| if File.directory?(path) true else if File.exist?(path) and ! File.directory?(path) self.debug "Search path #{path} is not a directory" else self.debug "Search path #{path} does not exist" end false end end end def search(name) paths.each { |path| fqname = File.join(path,name) begin stat = File.stat(fqname) rescue # should probably rescue specific errors... self.debug("Could not find #{name} in #{path}") next end # if we've gotten this far, we found a valid script return fqname } paths.each { |path| fqname_sh = File.join(path,"#{name}.sh") begin stat = File.stat(fqname_sh) rescue # should probably rescue specific errors... self.debug("Could not find #{name}.sh in #{path}") next end # if we've gotten this far, we found a valid script return fqname_sh } raise Puppet::Error, "Could not find init script for '#{name}'" end # The start command is just the init scriptwith 'start'. def startcmd [initscript, :start] end # The stop command is just the init script with 'stop'. def stopcmd [initscript, :stop] end def restartcmd (@resource[:hasrestart] == :true) && [initscript, :restart] end # If it was specified that the init script has a 'status' command, then # we just return that; otherwise, we return false, which causes it to # fallback to other mechanisms. def statuscmd (@resource[:hasstatus] == :true) && [initscript, :status] end +private + + def self.is_init?(script = initscript) + !File.symlink?(script) || File.readlink(script) != "/lib/init/upstart-job" + end end diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index 5e9512607..c5c33d262 100755 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -1,359 +1,340 @@ require 'semver' Puppet::Type.type(:service).provide :upstart, :parent => :debian do START_ON = /^\s*start\s+on/ COMMENTED_START_ON = /^\s*#+\s*start\s+on/ MANUAL = /^\s*manual\s*$/ 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 self.get_services(['wait-for-state']) end def self.get_services(exclude=[]) 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]}" elsif matcher = line.match(/^(network-interface-security)\s\(([^\)]+)\)/) "#{matcher[1]} JOB=#{matcher[2]}" else line.split.first end - instances << new(:name => name) unless exclude.include? name + instances << new(:name => name) } } - instances + instances.reject { |instance| exclude.include?(instance.name) } end def self.defpath ["/etc/init", "/etc/init.d"] end def upstart_version @@upstart_version ||= initctl("--version").match(/initctl \(upstart ([^\)]*)\)/)[1] end # Where is our override script? def overscript @overscript ||= initscript.gsub(/\.conf$/,".override") end def search(name) # Search prefers .conf as that is what upstart uses [".conf", "", ".sh"].each do |suffix| paths.each do |path| - if matcher = name.match(/^(network-interface|network-interface-security)/) - fqname = File.join(path, matcher[1] + suffix) - else - fqname = File.join(path, name + suffix) - end + service_name = name.match(/^(\S+)/)[1] + fqname = File.join(path, service_name + suffix) if File.exists?(fqname) return fqname end self.debug("Could not find #{name}#{suffix} in #{path}") end end raise Puppet::Error, "Could not find init script or upstart conf file for '#{name}'" end def enabled? return super if not is_upstart? script_contents = read_script_from(initscript) if version_is_pre_0_6_7 enabled_pre_0_6_7?(script_contents) elsif version_is_pre_0_9_0 enabled_pre_0_9_0?(script_contents) elsif version_is_post_0_9_0 enabled_post_0_9_0?(script_contents, read_override_file) end end def enable return super if not is_upstart? script_text = read_script_from(initscript) if version_is_pre_0_9_0 enable_pre_0_9_0(script_text) else enable_post_0_9_0(script_text, read_override_file) end end def disable return super if not is_upstart? script_text = read_script_from(initscript) if version_is_pre_0_6_7 disable_pre_0_6_7(script_text) elsif version_is_pre_0_9_0 disable_pre_0_9_0(script_text) elsif version_is_post_0_9_0 disable_post_0_9_0(read_override_file) end end def startcmd is_upstart? ? [command(:start), @resource[:name]] : super end def stopcmd is_upstart? ? [command(:stop), @resource[:name]] : super end def restartcmd 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 normal_status - ucommand(:status, false) - ($?.exitstatus == 0) ? :running : :stopped - end + return super if not is_upstart? - def upstart_status(exec = @resource[:name]) output = status_exec(@resource[:name].split) - if (! $?.nil?) && (output =~ /start\//) + if output =~ /start\// return :running else return :stopped end end +private def is_upstart?(script = initscript) - return true if (File.symlink?(script) && File.readlink(script) == "/lib/init/upstart-job") - return true if (File.file?(script) && (not script.include?("init.d"))) - return false + File.exists?(script) && script.match(/\/etc\/init\/\S+\.conf/) end -private - def version_is_pre_0_6_7 Puppet::Util::Package.versioncmp(upstart_version, "0.6.7") == -1 end def version_is_pre_0_9_0 Puppet::Util::Package.versioncmp(upstart_version, "0.9.0") == -1 end def version_is_post_0_9_0 Puppet::Util::Package.versioncmp(upstart_version, "0.9.0") >= 0 end def enabled_pre_0_6_7?(script_text) # Upstart version < 0.6.7 means no manual stanza. if script_text.match(START_ON) return :true else return :false end end def enabled_pre_0_9_0?(script_text) # Upstart version < 0.9.0 means no override files # So we check to see if an uncommented start on or manual stanza is the last one in the file # The last one in the file wins. enabled = :false script_text.each do |line| if line.match(START_ON) enabled = :true elsif line.match(MANUAL) enabled = :false end end enabled end def enabled_post_0_9_0?(script_text, over_text) # This version has manual stanzas and override files # So we check to see if an uncommented start on or manual stanza is the last one in the # conf file and any override files. The last one in the file wins. enabled = :false script_text.each do |line| if line.match(START_ON) enabled = :true elsif line.match(MANUAL) enabled = :false end end over_text.each do |line| if line.match(START_ON) enabled = :true elsif line.match(MANUAL) enabled = :false end end if over_text enabled end def enable_pre_0_9_0(text) # We also need to remove any manual stanzas to ensure that it is enabled text = remove_manual_from(text) if enabled_pre_0_9_0?(text) == :false enabled_script = if text.match(COMMENTED_START_ON) uncomment_start_block_in(text) else add_default_start_to(text) end else enabled_script = text end write_script_to(initscript, enabled_script) end def enable_post_0_9_0(script_text, over_text) over_text = remove_manual_from(over_text) if enabled_post_0_9_0?(script_text, over_text) == :false if script_text.match(START_ON) over_text << extract_start_on_block_from(script_text) else over_text << "\nstart on runlevel [2,3,4,5]" end end write_script_to(overscript, over_text) end def disable_pre_0_6_7(script_text) disabled_script = comment_start_block_in(script_text) write_script_to(initscript, disabled_script) end def disable_pre_0_9_0(script_text) write_script_to(initscript, ensure_disabled_with_manual(script_text)) end def disable_post_0_9_0(over_text) write_script_to(overscript, ensure_disabled_with_manual(over_text)) end def read_override_file if File.exists?(overscript) read_script_from(overscript) else "" end end def uncomment(line) line.gsub(/^(\s*)#+/, '\1') end def remove_trailing_comments_from_commented_line_of(line) line.gsub(/^(\s*#+\s*[^#]*).*/, '\1') end def remove_trailing_comments_from(line) line.gsub(/^(\s*[^#]*).*/, '\1') end def unbalanced_parens_on(line) line.count('(') - line.count(')') end def remove_manual_from(text) text.gsub(MANUAL, "") end def comment_start_block_in(text) parens = 0 text.map do |line| if line.match(START_ON) || parens > 0 # If there are more opening parens than closing parens, we need to comment out a multiline 'start on' stanza parens += unbalanced_parens_on(remove_trailing_comments_from(line)) "#" + line else line end end.join('') end def uncomment_start_block_in(text) parens = 0 text.map do |line| if line.match(COMMENTED_START_ON) || parens > 0 parens += unbalanced_parens_on(remove_trailing_comments_from_commented_line_of(line)) uncomment(line) else line end end.join('') end def extract_start_on_block_from(text) parens = 0 text.map do |line| if line.match(START_ON) || parens > 0 parens += unbalanced_parens_on(remove_trailing_comments_from(line)) line end end.join('') end def add_default_start_to(text) text + "\nstart on runlevel [2,3,4,5]" end def ensure_disabled_with_manual(text) remove_manual_from(text) + "\nmanual" end def read_script_from(filename) File.open(filename) do |file| file.read end end def write_script_to(file, text) Puppet::Util.replace_file(file, 0644) do |file| file.write(text) end end end diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb index 173fb8515..60c7e806d 100755 --- a/spec/unit/provider/service/init_spec.rb +++ b/spec/unit/provider/service/init_spec.rb @@ -1,170 +1,191 @@ #!/usr/bin/env rspec # # Unit testing for the Init service Provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:init) describe provider_class do before :each do @class = Puppet::Type.type(:service).provider(:init) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" -# @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns ["/service/path","/alt/service/path"] -# @resource.stubs(:ref).returns "Service[myservice]" File.stubs(:directory?).returns(true) @provider = provider_class.new @provider.resource = @resource end describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) @class.stubs(:defpath).returns('tmp') end + it "should return instances for all services" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} + @class.instances.should == results end + it "should omit an array of services from exclude list" do exclude = ['two', 'four'] - (@services-exclude).each do |inst| + (@services - exclude).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = (@services-exclude).collect {|x| "#{x}_instance"} + @class.get_services(@class.defpath, exclude).should == results end + it "should omit a single service from the exclude list" do exclude = 'two' - (@services-[exclude]).each do |inst| + (@services - [exclude]).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end - results = @services.reject{|x| x==exclude }.collect {|x| "#{x}_instance"} + results = @services.reject{|x| x == exclude }.collect {|x| "#{x}_instance"} + @class.get_services(@class.defpath, exclude).should == results end + it "should use defpath" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:path] == @class.defpath}.returns("#{inst}_instance") end results = @services.sort.collect {|x| "#{x}_instance"} + @class.instances.sort.should == results end + it "should set hasstatus to true for providers" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:hasstatus] == true}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} + + @class.instances.should == results + end + + it "should discard upstart jobs" do + not_init_service, *valid_services = @services + valid_services.each do |inst| + @class.expects(:new).with{|hash| hash[:name] == inst && hash[:hasstatus] == true}.returns("#{inst}_instance") + end + File.stubs(:symlink?).returns(false) + File.stubs(:symlink?).with("tmp/#{not_init_service}").returns(true) + File.stubs(:readlink).with("tmp/#{not_init_service}").returns("/lib/init/upstart-job") + + results = valid_services.collect {|x| "#{x}_instance"} @class.instances.should == results end end describe "when searching for the init script" do it "should discard paths that do not exist" do File.stubs(:exist?).returns(false) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should discard paths that are not directories" do File.stubs(:exist?).returns(true) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/service/path/myservice").returns true @provider.initscript.should == "/service/path/myservice" end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/alt/service/path/myservice").returns true @provider.initscript.should == "/alt/service/path/myservice" end it "should fail if the service isn't there" do lambda { @provider.initscript }.should raise_error(Puppet::Error, "Could not find init script for 'myservice'") end end describe "if the init script is present" do before :each do File.stubs(:stat).with("/service/path/myservice").returns true end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should pass #{method} to the init script when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ["/service/path/myservice",method]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the command" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process" do @provider.expects(:texecute).with(:stop, ['/service/path/myservice', :stop ], true).returns("") @provider.expects(:texecute).with(:start,['/service/path/myservice', :start], true).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.restart end end end end diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index d4d60aa4e..1cdaa3c09 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -1,505 +1,509 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:upstart) describe provider_class, :unless => Puppet.features.microsoft_windows? do let(:manual) { "\nmanual" } let(:start_on_default_runlevels) { "\nstart on runlevel [2,3,4,5]" } def given_contents_of(file, content) File.open(file, 'w') do |file| file.write(content) end end def then_contents_of(file) File.open(file).read end 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") + processes = "network-interface (eth0)" provider_class.stubs(:execpipe).yields(processes) provider_class.instances.first.name.should == "network-interface INTERFACE=eth0" end it "should attach the job name for network interface security" do - processes = ["network-interface-security (network-interface/eth0)"].join("\n") + processes = "network-interface-security (network-interface/eth0)" provider_class.stubs(:execpipe).yields(processes) provider_class.instances.first.name.should == "network-interface-security JOB=network-interface/eth0" end + + it "should not find excluded services" do + processes = "wait-for-state stop/waiting" + provider_class.stubs(:execpipe).yields(processes) + provider_class.instances.should be_empty + 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") + describe "#search" do + it "searches through paths to find a matching conf file" do + File.stubs(:directory?).returns(true) + File.stubs(:exists?).returns(false) + File.expects(:exists?).with("/etc/init/foo-bar.conf").returns(true) + resource = Puppet::Type.type(:service).new(:name => "foo-bar", :provider => :upstart) 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 + provider.initscript.should == "/etc/init/foo-bar.conf" + end + + it "searches for just the name of a compound named service" do + File.stubs(:directory?).returns(true) + File.stubs(:exists?).returns(false) + File.expects(:exists?).with("/etc/init/network-interface.conf").returns(true) + resource = Puppet::Type.type(:service).new(:name => "network-interface INTERFACE=lo", :provider => :upstart) + provider = provider_class.new(resource) + + provider.initscript.should == "/etc/init/network-interface.conf" end + end + describe "#status" do 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 describe "should be enableable" do let :resource do Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) end let :provider do provider_class.new(resource) end let :init_script do PuppetSpec::Files.tmpfile("foo.conf") end let :over_script do PuppetSpec::Files.tmpfile("foo.override") end let :disabled_content do "\t # \t start on\nother file stuff" end let :multiline_disabled do "# \t start on other file stuff (\n" + "# more stuff ( # )))))inline comment\n" + "# finishing up )\n" + "# and done )\n" + "this line shouldn't be touched\n" end let :multiline_disabled_bad do "# \t start on other file stuff (\n" + "# more stuff ( # )))))inline comment\n" + "# finishing up )\n" + "# and done )\n" + "# this is a comment i want to be a comment\n" + "this line shouldn't be touched\n" end let :multiline_enabled_bad do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" + "# this is a comment i want to be a comment\n" + "this line shouldn't be touched\n" end let :multiline_enabled do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" + "this line shouldn't be touched\n" end let :multiline_enabled_standalone do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" end let :enabled_content do "\t \t start on\nother file stuff" end let :content do "just some text" end describe "Upstart version < 0.6.7" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.6.5") provider.stubs(:search).returns(init_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should open and uncomment the '#start on' line" do given_contents_of(init_script, disabled_content) provider.enable then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) provider.enable then_contents_of(init_script).should == multiline_enabled_bad end end describe "when disabling" do it "should open and comment the 'start on' line" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == "#" + enabled_content end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_disabled end end describe "when checking whether it is enabled" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end end describe "Upstart version < 0.9.0" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.7.0") provider.stubs(:search).returns(init_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should open and uncomment the '#start on' line" do given_contents_of(init_script, disabled_content) provider.enable then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should remove manual stanzas" do given_contents_of(init_script, multiline_enabled + manual) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) provider.enable then_contents_of(init_script).should == multiline_enabled_bad end end describe "when disabling" do it "should add a manual stanza" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == enabled_content + manual end it "should remove manual stanzas before adding new ones" do given_contents_of(init_script, multiline_enabled + manual + "\n" + multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled + "\n" + multiline_enabled + manual end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled + manual end end describe "when checking whether it is enabled" do describe "with no manual stanza" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end describe "with manual stanza" do it "should consider 'start on ...' to be disabled if there is a trailing manual stanza" do given_contents_of(init_script, enabled_content + manual + "\nother stuff") provider.enabled?.should == :false end it "should consider two start on lines with a manual in the middle to be enabled" do given_contents_of(init_script, enabled_content + manual + "\n" + enabled_content) provider.enabled?.should == :true end end end end describe "Upstart version > 0.9.0" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.9.5") provider.stubs(:search).returns(init_script) provider.stubs(:overscript).returns(over_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" then_contents_of(over_script).should == start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_disabled then_contents_of(over_script).should == start_on_default_runlevels end it "should remove any manual stanzas from the override file" do given_contents_of(over_script, manual) given_contents_of(init_script, enabled_content) provider.enable then_contents_of(init_script).should == enabled_content then_contents_of(over_script).should == "" end it "should copy existing start on from conf file if conf file is disabled" do given_contents_of(init_script, multiline_enabled_standalone + manual) provider.enable then_contents_of(init_script).should == multiline_enabled_standalone + manual then_contents_of(over_script).should == multiline_enabled_standalone end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) given_contents_of(over_script, "") provider.enable then_contents_of(init_script).should == multiline_disabled_bad then_contents_of(over_script).should == start_on_default_runlevels end end describe "when disabling" do it "should add a manual stanza to the override file" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == enabled_content then_contents_of(over_script).should == manual end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled then_contents_of(over_script).should == manual end end describe "when checking whether it is enabled" do describe "with no override file" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end describe "with override file" do it "should consider 'start on ...' to be disabled if there is manual in override file" do given_contents_of(init_script, enabled_content) given_contents_of(over_script, manual + "\nother stuff") provider.enabled?.should == :false end it "should consider '#start on ...' to be enabled if there is a start on in the override file" do given_contents_of(init_script, disabled_content) given_contents_of(over_script, "start on stuff") provider.enabled?.should == :true end end end end end end