diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index e38b43e38..a767d7cc6 100755 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -1,344 +1,331 @@ +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 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 self.defpath - ["/etc/init.d", "/etc/init"] + ["/etc/init", "/etc/init.d"] end def upstart_version - @@upstart_version ||= Puppet::Util.execute(command(:initctl) + " --version", {true, true}).match(/initctl \(upstart (\d\.\d[\.\d]?)\)/)[1] + @@upstart_version ||= SemVer.new(initctl(" --version").match(/initctl \(upstart (\d\.\d[\.\d]?)\)/)[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 { |path| + paths.each do |path| fqname = File.join(path,name+suffix) - begin - stat = File.stat(fqname) - rescue - # should probably rescue specific errors... - self.debug("Could not find #{name}#{suffix} in #{path}") - next + if File.exists?(fqname) + return fqname end - # if we've gotten this far, we found a valid script - return fqname - } + 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? - if is_upstart? - if Puppet::Util::Package.versioncmp(upstart_version, "0.6.7") == -1 - # Upstart version < 0.6.7 means no manual stanza. - if File.open(initscript).read.match(/^\s*start\s+on/) - return :true - else - return :false - end - elsif upstart_version < "0.9.0" - # 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 - File.open(initscript).read.each do |line| - if line.match(/^\s*start\s+on/) - enabled = :true - elsif line.match(/^\s*manual\s*$/) - enabled = :false - end - end - enabled - else - # 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 - @conf_enabled = false - script_text = File.open(initscript).read - begin - over_text = File.open(overscript).read - rescue - over_text = nil - end - - script_text.each do |line| - if line.match(/^\s*start\s+on/) - enabled = :true - @conf_enabled = true - elsif line.match(/^\s*manual\s*$/) - enabled = :false - @conf_enabled = false - end - end - over_text.each do |line| - if line.match(/^\s*start\s+on/) - enabled = :true - elsif line.match(/^\s*manual\s*$/) - enabled = :false - end - end if over_text - enabled - end - else - super + return super if not is_upstart? + + script_contents = File.open(initscript).read + if upstart_version < "0.6.7" + enabled_pre_0_6_7?(script_contents) + elsif upstart_version < "0.9.0" + enabled_pre_0_9_0?(script_contents) + elsif upstart_version >= "0.9.0" + enabled_post_0_9_0?(script_contents, read_override_file) end end def enable - if is_upstart? - script_text = File.open(initscript).read - # Parens is needed to match parens in a multiline upstart start on stanza - parens = 0 - if Puppet::Util::Package.versioncmp(upstart_version, "0.9.0") == -1 - enabled_script = - # Two cases, either there is a start on line already or we need to add one - if script_text.to_s.match(/^\s*#*\s*start\s+on/) - script_text.map do |line| - # t_line is used for paren counting and chops off any trailing comments before counting parens - t_line = line.gsub(/^(\s*#+\s*[^#]*).*/, '\1') - if line.match(/^\s*#+\s*start\s+on/) - # If there are more opening parens than closing parens, we need to uncomment a multiline 'start on' stanzas. - if (t_line.count('(') > t_line.count(')') ) - parens = t_line.count('(') - t_line.count(')') - end - line.gsub(/^(\s*)#+(\s*start\s+on)/, '\1\2') - elsif parens > 0 - # If there are still more opening than closing parens we need to continue uncommenting lines - parens += (t_line.count('(') - t_line.count(')') ) - line.gsub(/^(\s*)#+/, '\1') - else - line - end - end - else - # If there is no "start on" it isn't enabled and needs that line added - script_text.to_s + "\nstart on runlevel [2,3,4,5]" - end - - unless Puppet::Util::Package.versioncmp(upstart_version, "0.6.7") == -1 - # We also need to remove any manual stanzas to ensure that it is enabled - enabled_script.each do |line| - line.gsub!(/^\s*manual\s*$/, "") - end - end + return super if not is_upstart? - Puppet::Util.replace_file(initscript, 0644) do |file| - file.write(enabled_script) - end - - else - # We have override files in this case. So this breaks down to the following cases... - # 1.) conf has 'start on' and no 'manual', override has 'manual' => remove 'manual' from override - # 2.) conf has 'start on' and 'manual', override may or may not have 'manual' => - # remove 'manual' from override if present, copy 'start on' from conf to override - # 3.) conf has no 'start on', override has 'manual' or no 'start on' => remove manual if present, add 'start on' - # 4.) conf has no 'start on', override has 'manual' and has 'start on' => remove manual - begin - over_text = File.open(overscript).read - rescue - over_text = nil - end - - if script_text.match(/^\s*start\s+on/) and not script_text.match(/^\s*manual\s*$/) - # Case #1 from above - override has manual - over_text.gsub!(/^\s*manual\s*$/,"") if over_text - elsif script_text.match(/^\s*start\s+on/) and script_text.match(/^\s*manual\s*$/) - # Case #2 from above - # If the conf file was already enabled, all we need to do is remove the manual stanza from the override file - if @conf_enabled - # Remove any manual stanzas from the override file - over_text.gsub!(/^\s*manual\s*$/,"") if over_text - else - # If the override has no start stanza, copy it from the conf file - # First, copy the start on lines from the conf file - start_on = script_text.map do |line| - t_line = line.gsub(/^([^#]*).*/, '\1') - - if line.match(/^\s*start\s+on/) - if (t_line.count('(') > t_line.count(')') ) - parens = t_line.count('(') - t_line.count(')') - end - line - elsif parens > 0 - parens += (t_line.count('(') - t_line.count(')') ) - line - end - end - - # Remove any manual stanzas from the override file - over_text.gsub!(/^\s*manual\s*$/,"") if over_text - - # Add the copied 'start on' stanza if needed - over_text << start_on.to_s if over_text and not over_text.match(/^\s*start\s+on/) - over_text = start_on.to_s unless over_text - end - elsif not script_text.match(/^\s*start\s+on/) - # Case #3 and #4 from above - over_text.gsub!(/^\s*manual\s*$/,"") if over_text - over_text << "\nstart on runlevel [2,3,4,5]" if over_text and not over_text.match(/^\s*start\s+on/) - over_text = "\nstart on runlevel [2,3,4,5]" unless over_text - end - - Puppet::Util.replace_file(overscript, 0644) do |file| - file.write(over_text) - end - end + script_text = File.open(initscript).read + if upstart_version < "0.9.0" + enable_pre_0_9_0(script_text) else - super + enable_post_0_9_0(script_text, read_override_file) end end def disable - if is_upstart? - # Parens is needed to match parens in a multiline upstart start on stanza - parens = 0 - script_text = File.open(initscript).read - - if Puppet::Util::Package.versioncmp(upstart_version, "0.6.7") == -1 - disabled_script = script_text.map do |line| - t_line = line.gsub(/^([^#]*).*/, '\1') - if line.match(/^\s*start\s+on/) - # If there are more opening parens than closing parens, we need to comment out a multiline 'start on' stanza - if (t_line.count('(') > t_line.count(')') ) - parens = t_line.count('(') - t_line.count(')') - end - line.gsub(/^(\s*start\s+on)/, '#\1') - elsif parens > 0 - # If there are still more opening than closing parens we need to continue uncommenting lines - parens += (t_line.count('(') - t_line.count(')') ) - "#" << line - else - line - end - end - - Puppet::Util.replace_file(initscript, 0644) do |file| - file.write(disabled_script) - end - elsif Puppet::Util::Package.versioncmp(upstart_version, "0.9.0") == -1 - disabled_script = script_text.gsub(/^\s*manual\s*$/,"") - disabled_script << "\nmanual" - - Puppet::Util.replace_file(initscript, 0644) do |file| - file.write(disabled_script) - end - else - # We have override files in this case. - # So we remove any existing manual stanzas and add one at the end - begin - over_text = File.open(overscript).read - rescue - over_text = nil - end - - # First, remove any manual stanzas - over_text.gsub!(/^\s*manual\s*$/,"") if over_text - - # Then add a manual stanza at the end. - over_text << "\nmanual" if over_text - over_text = "manual" unless over_text - - Puppet::Util.replace_file(overscript, 0644) do |file| - file.write(over_text) - end - end - else - super + return super if not is_upstart? + + script_text = File.open(initscript).read + if upstart_version < "0.6.7" + disable_pre_0_6_7(script_text) + elsif upstart_version < "0.9.0" + disable_pre_0_9_0(script_text) + elsif upstart_version >= "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 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?(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 end +private + + 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) + File.open(overscript).read + 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 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/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index 8031075db..55cb4150d 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -1,460 +1,499 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:upstart) describe provider_class 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") 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 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 - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close + given_contents_of(init_script, disabled_content) + provider.enable - File.open(init_script).read.should == enabled_content + + then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do - file = File.open(init_script, 'w') - file.write("this is a file") - file.close + given_contents_of(init_script, "this is a file") + provider.enable - File.open(init_script).read.should == "this is a file\nstart on runlevel [2,3,4,5]" + + then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_disabled) - file.close + given_contents_of(init_script, multiline_disabled) + provider.enable - File.open(init_script).read.should == multiline_enabled + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.disable - File.open(init_script).read.should == "#" + enabled_content + + then_contents_of(init_script).should == "#" + enabled_content end it "should handle multiline 'start on' stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_enabled) - file.close + given_contents_of(init_script, multiline_enabled) + provider.disable - File.open(init_script).read.should == multiline_disabled + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close + given_contents_of(init_script, disabled_content) + provider.enabled?.should == :false end it "should consider no start on line to be disabled" do - file = File.open(init_script, 'w') - file.write(content) - file.close + 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 - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close + given_contents_of(init_script, disabled_content) + provider.enable - File.open(init_script).read.should == enabled_content + + then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do - file = File.open(init_script, 'w') - file.write("this is a file") - file.close + given_contents_of(init_script, "this is a file") + provider.enable - File.open(init_script).read.should == "this is a file\nstart on runlevel [2,3,4,5]" + + then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_disabled) - file.close + given_contents_of(init_script, multiline_disabled) + provider.enable - File.open(init_script).read.should == multiline_enabled + + then_contents_of(init_script).should == multiline_enabled end it "should remove manual stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_enabled + "\nmanual") - file.close + given_contents_of(init_script, multiline_enabled + manual) + provider.enable - File.open(init_script).read.should == multiline_enabled + "\n" + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.disable - File.open(init_script).read.should == enabled_content + "\nmanual" + + then_contents_of(init_script).should == enabled_content + manual end it "should remove manual stanzas before adding new ones" do - file = File.open(init_script, 'w') - file.write(multiline_enabled + "\nmanual\n" + multiline_enabled) - file.close + given_contents_of(init_script, multiline_enabled + manual + "\n" + multiline_enabled) + provider.disable - File.open(init_script).read.should == multiline_enabled + "\n" + multiline_enabled + "\nmanual" + + then_contents_of(init_script).should == multiline_enabled + "\n" + multiline_enabled + manual end it "should handle multiline 'start on' stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_enabled) - file.close + given_contents_of(init_script, multiline_enabled) + provider.disable - File.open(init_script).read.should == multiline_enabled + "\nmanual" + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close + given_contents_of(init_script, disabled_content) + provider.enabled?.should == :false end it "should consider no start on line to be disabled" do - file = File.open(init_script, 'w') - file.write(content) - file.close + 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 - file = File.open(init_script, 'w') - file.write(enabled_content + "\nmanual\nother stuff") - file.close + 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 - file = File.open(init_script, 'w') - file.write(enabled_content + "\nmanual\n" + enabled_content) - file.close + given_contents_of(init_script, enabled_content + manual + "\n" + enabled_content) + provider.enabled?.should == :true - end + 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 - file = File.open(init_script, 'w') - file.write("this is a file") - file.close + given_contents_of(init_script, "this is a file") + provider.enable - File.open(init_script).read.should == "this is a file" - File.open(over_script).read.should == "\nstart on runlevel [2,3,4,5]" + + 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 - file = File.open(init_script, 'w') - file.write(multiline_disabled) - file.close + given_contents_of(init_script, multiline_disabled) + provider.enable - File.open(init_script).read.should == multiline_disabled - File.open(over_script).read.should == "\nstart on runlevel [2,3,4,5]" + + 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 - file = File.open(over_script, 'w') - file.write("\nmanual") - file.close - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(over_script, manual) + given_contents_of(init_script, enabled_content) + provider.enable - File.open(init_script).read.should == enabled_content - File.open(over_script).read.should == "" + + 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 - file = File.open(init_script, 'w') - file.write(multiline_enabled_standalone + "\nmanual") - file.close + given_contents_of(init_script, multiline_enabled_standalone + manual) + provider.enable - File.open(init_script).read.should == multiline_enabled_standalone + "\nmanual" - File.open(over_script).read.should == multiline_enabled_standalone + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.disable - File.open(init_script).read.should == enabled_content - File.open(over_script).read.should == "\nmanual" + + then_contents_of(init_script).should == enabled_content + then_contents_of(over_script).should == manual end it "should handle multiline 'start on' stanzas" do - file = File.open(init_script, 'w') - file.write(multiline_enabled) - file.close + given_contents_of(init_script, multiline_enabled) + provider.disable - File.open(init_script).read.should == multiline_enabled - File.open(over_script).read.should == "\nmanual" + + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close + given_contents_of(init_script, enabled_content) + provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close + given_contents_of(init_script, disabled_content) + provider.enabled?.should == :false end it "should consider no start on line to be disabled" do - file = File.open(init_script, 'w') - file.write(content) - file.close + 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 - file = File.open(init_script, 'w') - file.write(enabled_content) - file.close - file = File.open(over_script, 'w') - file.write("\nmanual\nother stuff") - file.close + 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 - file = File.open(init_script, 'w') - file.write(disabled_content) - file.close - file = File.open(over_script, 'w') - file.write("start on stuff") - file.close + 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