summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom May <thom@chef.io>2018-04-20 14:24:46 +0100
committerThom May <thom@chef.io>2018-04-23 15:26:36 +0100
commitb2f65af035d501d4da3b3c2e2c955b18044fc073 (patch)
tree60ba903df2bf4ba7a2aeff1927db84ceb07ed684
parentb6ee427ea8dae1eaaf3bbd0e96559d983bb9f908 (diff)
downloadchef-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.rb45
-rw-r--r--spec/unit/provider/service/debian_service_spec.rb202
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