diff options
author | Thom May <thom@chef.io> | 2018-04-20 14:24:46 +0100 |
---|---|---|
committer | Thom May <thom@chef.io> | 2018-04-23 15:26:36 +0100 |
commit | b2f65af035d501d4da3b3c2e2c955b18044fc073 (patch) | |
tree | 60ba903df2bf4ba7a2aeff1927db84ceb07ed684 | |
parent | b6ee427ea8dae1eaaf3bbd0e96559d983bb9f908 (diff) | |
download | chef-b2f65af035d501d4da3b3c2e2c955b18044fc073.tar.gz |
[Debian/Ubuntu] Detect init script service levelstm/ub_1404_service
Debian removed the no-op flag from update-rc.d, and so we're now
unlinking services rather than just querying them. Just parsing the
scripts ourselves is much safer.
Fixes: #7119
Signed-off-by: Thom May <thom@chef.io>
-rw-r--r-- | lib/chef/provider/service/debian.rb | 45 | ||||
-rw-r--r-- | spec/unit/provider/service/debian_service_spec.rb | 202 |
2 files changed, 72 insertions, 175 deletions
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb index 01a1ed3ad9..09af9f224f 100644 --- a/lib/chef/provider/service/debian.rb +++ b/lib/chef/provider/service/debian.rb @@ -52,8 +52,8 @@ class Chef end requirements.assert(:all_actions) do |a| - a.assertion { @so_priority.exitstatus == 0 } - a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -f #{current_resource.service_name} failed - #{@so_priority.inspect}" + a.assertion { @got_priority == true } + a.failure_message Chef::Exceptions::Service, "Unable to determine priority for service" # This can happen if the service is not yet installed,so we'll fake it. a.whyrun ["Unable to determine priority of service, assuming service would have been correctly installed earlier in the run.", "Assigning temporary priorities to continue.", @@ -70,22 +70,38 @@ class Chef end end + # returns a list of levels that the service should be stopped or started on + def parse_init_file(path) + return [] unless ::File.exist?(path) + in_info = false + ::File.readlines(path).each_with_object([]) do |line, acc| + if line =~ /^### BEGIN INIT INFO/ + in_info = true + elsif line =~ /^### END INIT INFO/ + in_info = false + elsif in_info + if line =~ /Default-(Start|Stop):\s+(\d.*)/ + acc << $2.split(" ") + end + end + end.flatten + end + def get_priority priority = {} + rc_files = [] - @so_priority = shell_out!("/usr/sbin/update-rc.d -f #{current_resource.service_name} remove") + levels = parse_init_file(@init_command) + levels.each do |level| + rc_files.push Dir.glob("/etc/rc#{level}.d/[SK][0-9][0-9]#{current_resource.service_name}") + end - [@so_priority.stdout, @so_priority.stderr].each do |iop| - iop.each_line do |line| - if UPDATE_RC_D_PRIORITIES =~ line - # priority[runlevel] = [ S|K, priority ] - # S = Start, K = Kill - # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot - priority[$1] = [($2 == "S" ? :start : :stop), $3] - end - if line =~ UPDATE_RC_D_ENABLED_MATCHES - enabled = true - end + rc_files.flatten.each do |line| + if UPDATE_RC_D_PRIORITIES =~ line + # priority[runlevel] = [ S|K, priority ] + # S = Start, K = Kill + # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot + priority[$1] = [($2 == "S" ? :start : :stop), $3] end end @@ -95,6 +111,7 @@ class Chef priority = priority[2].last end + @got_priority = true priority end diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb index d944192755..25e7c9acc2 100644 --- a/spec/unit/provider/service/debian_service_spec.rb +++ b/spec/unit/provider/service/debian_service_spec.rb @@ -32,6 +32,17 @@ describe Chef::Provider::Service::Debian do @provider.current_resource = @current_resource @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil + allow(File).to receive(:exist?).with("/etc/init.d/chef").and_return true + end + + let(:init_lines) do + [ + "### BEGIN INIT INFO", + "# Required-Start: hostname $local_fs", + "# Default-Start: 2 3 4 5", + "# Default-Stop: 0 1 6", + "### END INIT INFO", + ] end describe "load_current_resource" do @@ -47,22 +58,14 @@ describe Chef::Provider::Service::Debian do context "when update-rc.d shows init linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) + allow(File).to receive(:readlines).with("/etc/init.d/chef").and_return(init_lines) - 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 = result - @stderr = "" - @status = double("Status", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr) - allow(@provider).to receive(:shell_out!).and_return(@status) + [0, 1, 6].each do |stop| + allow(Dir).to receive(:glob).with("/etc/rc#{stop}.d/[SK][0-9][0-9]chef").and_return(["/etc/rc#{stop}.d/K20chef"]) + end + [2, 3, 4, 5].each do |start| + allow(Dir).to receive(:glob).with("/etc/rc#{start}.d/[SK][0-9][0-9]chef").and_return(["/etc/rc#{start}.d/S20chef"]) + end end it "says the service is enabled" do @@ -74,14 +77,34 @@ describe Chef::Provider::Service::Debian do expect(@provider.load_current_resource).to equal(@current_resource) expect(@current_resource.enabled).to be_truthy end + + it "stores the start/stop priorities of the service" do + @provider.load_current_resource + expect(@provider.current_resource.priority).to eq( + { + "2" => [:start, "20"], + "3" => [:start, "20"], + "4" => [:start, "20"], + "5" => [:start, "20"], + "0" => [:stop, "20"], + "1" => [:stop, "20"], + "6" => [:stop, "20"] + }) + end end context "when update-rc.d shows init isn't linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) - @stdout = " Removing any system startup links for /etc/init.d/chef ..." - @status = double("Status", :exitstatus => 0, :stdout => @stdout, stderr: "") - allow(@provider).to receive(:shell_out!).and_return(@status) + + allow(File).to receive(:readlines).with("/etc/init.d/chef").and_return(init_lines) + + [0, 1, 6].each do |stop| + allow(Dir).to receive(:glob).with("/etc/rc#{stop}.d/[SK][0-9][0-9]chef").and_return([]) + end + [2, 3, 4, 5].each do |start| + allow(Dir).to receive(:glob).with("/etc/rc#{start}.d/[SK][0-9][0-9]chef").and_return([]) + end end it "says the service is disabled" do @@ -95,149 +118,6 @@ describe Chef::Provider::Service::Debian do end end - context "when update-rc.d fails" do - before do - @status = double("Status", exitstatus: -1, stdout: "", stderr: "") - allow(@provider).to receive(:shell_out!).and_return(@status) - end - - it "raises an error" do - @provider.load_current_resource - @provider.define_resource_requirements - expect do - @provider.process_resource_requirements - end.to raise_error(Chef::Exceptions::Service) - end - end - - { "Debian/Lenny and older" => { - "linked" => { - "stdout" => <<-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 - STDOUT - "stderr" => "", - "priorities" => { - "0" => [:stop, "20"], - "1" => [:stop, "20"], - "2" => [:start, "20"], - "3" => [:start, "20"], - "4" => [:start, "20"], - "5" => [:start, "20"], - "6" => [:stop, "20"], - }, - }, - "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" => <<-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 - STDERR - "priorities" => { - "0" => [:stop, "20"], - "1" => [:stop, "20"], - "2" => [:start, "20"], - "3" => [:start, "20"], - "4" => [:start, "20"], - "5" => [:start, "20"], - "6" => [:stop, "20"], - }, - }, - "not linked" => { - "stdout" => "update-rc.d: using dependency based boot sequencing", - "stderr" => "", - }, - }, - "Debian/Wheezy and earlier, a service only starting at run level S" => { - "linked" => { - "stdout" => "", - "stderr" => <<-STDERR, -insserv: remove service /etc/init.d/../rc0.d/K06rpcbind -insserv: remove service /etc/init.d/../rc1.d/K06rpcbind -insserv: remove service /etc/init.d/../rc6.d/K06rpcbind -insserv: remove service /etc/init.d/../rcS.d/S13rpcbind -insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop - STDERR - "priorities" => { - "0" => [:stop, "06"], - "1" => [:stop, "06"], - "6" => [:stop, "06"], - "S" => [:start, "13"], - }, - }, - "not linked" => { - "stdout" => "", - "stderr" => "insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop", - }, - }, - }.each do |model, expected_results| - context "on #{model}" do - context "when update-rc.d shows init linked to rc*.d/" do - before do - allow(@provider).to receive(:assert_update_rcd_available) - - @stdout = expected_results["linked"]["stdout"] - @stderr = expected_results["linked"]["stderr"] - @status = double("Status", exitstatus: 0, stdout: @stdout, stderr: @stderr) - allow(@provider).to receive(:shell_out!).and_return(@status) - end - - it "says the service is enabled" do - expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_truthy - end - - it "stores the 'enabled' state" do - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - expect(@provider.load_current_resource).to equal(@current_resource) - expect(@current_resource.enabled).to be_truthy - end - - it "stores the start/stop priorities of the service" do - @provider.load_current_resource - expect(@provider.current_resource.priority).to eq(expected_results["linked"]["priorities"]) - end - end - - context "when update-rc.d shows init isn't linked to rc*.d/" do - before do - allow(@provider).to receive(:assert_update_rcd_available) - @stdout = expected_results["not linked"]["stdout"] - @stderr = expected_results["not linked"]["stderr"] - @status = double("Status", exitstatus: 0, stdout: @stdout, stderr: @stderr) - allow(@provider).to receive(:shell_out!).and_return(@status) - end - - it "says the service is disabled" do - expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_falsey - end - - it "stores the 'disabled' state" do - allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) - expect(@provider.load_current_resource).to equal(@current_resource) - expect(@current_resource.enabled).to be_falsey - end - end - end - end - end describe "action_enable" do |