summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrobuye <rulejczyk@gmail.com>2019-09-17 11:54:23 +0200
committerrobuye <rulejczyk@gmail.com>2019-09-17 14:44:26 +0200
commitec177edf55504e56de1ba1319f5eca8351614e46 (patch)
tree0fddc22c69ca0444a1ccf1384fe71f93914871d5
parent1c642e292f1356454e0e226c7d33c3bb45dec8fb (diff)
downloadchef-ec177edf55504e56de1ba1319f5eca8351614e46.tar.gz
bring back support for legacy update-rc.d syntax
Legacy syntax is required to support start & stop priorities on very old systems running SysVinit. If an old enough version is detected Chef will prefer legacy syntax, otherwise it will use modern syntax and the priorities will be for the most part ignored. See https://lists.debian.org/debian-devel/2013/05/msg01109.html for additional context. To use legacy syntax sysv-rc package must be installed and it's version must be < 2.88. If the package is not installed at all then update-rc.d is coming from 'init-system-helpers' package and it's at least 2.88dsf-59.2 so it supports only modern syntax. Note that start|stop syntax has been dropped in 2.88dsf-42 and older 2.88 versions could work, but the email linked above indicates it didn't do anything. Signed-off-by: Rob Ulejczyk <rulejczyk@gmail.com>
-rw-r--r--lib/chef/provider/service/debian.rb86
-rw-r--r--spec/unit/provider/service/debian_service_spec.rb185
2 files changed, 215 insertions, 56 deletions
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index 6774b214ae..0fea83ab0f 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -149,43 +149,103 @@ class Chef
end
def enable_service
+ # We call the same command regardless if we're enabling or disabling
+ # Users passing a Hash are responsible for setting their own stop priorities
if new_resource.priority.is_a? Hash
- # we call the same command regardless of we're enabling or disabling
- # users passing a Hash are responsible for setting their own start priorities
set_priority
- else # No priority or Integer. Either way go with update-rc.d defaults
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
+ return
end
+
+ start_priority = new_resource.priority.is_a?(Integer) ? new_resource.priority : 20
+ # Stop processes in reverse order of start using '100 - start_priority'.
+ stop_priority = 100 - start_priority
+
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults #{start_priority} #{stop_priority}")
end
def disable_service
if new_resource.priority.is_a? Hash
- # we call the same command regardless of we're enabling or disabling
- # users passing a Hash are responsible for setting their own stop priorities
+ # We call the same command regardless if we're enabling or disabling
+ # Users passing a Hash are responsible for setting their own stop priorities
set_priority
- else # No priority or Integer. Either way disable in all levels.
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+ return
+ end
+
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+
+ # Use legacy syntax if update-rc.d supports it for backward compatibility.
+ if use_legacy_update_rc_d?
+ # If no priority was given assume 20 (update-rc.d default).
+ start_priority = new_resource.priority.is_a?(Integer) ? new_resource.priority : 20
+ # Stop processes in reverse order of start using '100 - start_priority'.
+ stop_priority = 100 - start_priority
+
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop #{stop_priority} 2 3 4 5 .")
+ else
shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} disable")
end
end
def set_priority
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+
+ # Use legacy syntax if update-rc.d supports it for backward compatibility.
+ if use_legacy_update_rc_d?
+ args = ""
+ new_resource.priority.each do |level, o|
+ action = o[0]
+ priority = o[1]
+ args += "#{action} #{priority} #{level} . "
+ end
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{args}")
+ return
+ end
+
+ # Use modern syntax, ignoring priorities as update-rc.d does not support it.
+ #
# Reset priorities to default values before applying customizations. This way
# the final state will always be consistent, regardless if all runlevels were
# provided.
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
-
- # Note the `_priority` is not used. update-rc.d does not support priorities
- # anymore, this feature has been dropped in sysvinit 2.88dsf-42.
new_resource.priority.each do |level, (action, _priority)|
disable_or_enable = (action == :start ? "enable" : "disable")
shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{disable_or_enable} #{level}")
end
end
+
+ # Ancient Debian releases used run levels and priorities to manage dependencies ordering.
+ # Old syntax no longer works and new syntax does not support priorities. If Chef detects
+ # ancient update-rc.d it will prefer legacy syntax so priorities can be set correctly in
+ # case the host is in fact running SysVinit.
+ #
+ # Additional context: https://lists.debian.org/debian-devel/2013/05/msg01109.html
+ def use_legacy_update_rc_d?
+ @sysv_rc_version ||= shell_out!("dpkg-query -W --showformat '${Version}' sysv-rc").stdout.strip
+
+ # sysv-rc is not installed therefore we're on modern Debian and legacy syntax does not work
+ if @sysv_rc_version.empty?
+ logger.trace("sysv-rc package is not installed. update-rc.d will use modern syntax")
+ return false
+ end
+
+ # sysv-rc is installed and update-rc.d is old enough to support legacy syntax and features
+ if @sysv_rc_version.to_f < 2.88
+ logger.trace("sysv-rc #{@sysv_rc_version} detected. update-rc.d will use legacy syntax")
+ return true
+ end
+
+ # sysv-rc 2.88dsf-42 drops the legacy syntax
+ if @sysv_rc_version.to_f == 2.88 && @sysv_rc_version[8..9].to_i < 42
+ logger.trace("sysv-rc #{@sysv_rc_version} detected. update-rc.d will use legacy syntax")
+ return true
+ end
+
+ # default to modern syntax
+ false
+ end
end
end
end
diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb
index 0aef4bc760..a69909107c 100644
--- a/spec/unit/provider/service/debian_service_spec.rb
+++ b/spec/unit/provider/service/debian_service_spec.rb
@@ -183,88 +183,187 @@ describe Chef::Provider::Service::Debian do
describe "enable_service" do
let(:service_name) { @new_resource.service_name }
context "when the service doesn't set a priority" do
- it "calls update-rc.d remove => defaults" do
+ it "assumes default priority 20 and calls update-rc.d remove => defaults 20 80" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} defaults 20 80",
])
@provider.enable_service
end
end
- context "when the service sets a simple priority" do
+ context "when the service sets a simple priority 75" do
before do
@new_resource.priority(75)
end
- it "calls update-rc.d remove => defaults" do
+ it "calls update-rc.d remove => defaults 75 25" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} defaults 75 25",
])
@provider.enable_service
end
end
- context "when the service sets complex priorities" do
+ context "when the service sets complex priorities using Hash" do
before do
@new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
end
- it "calls update-rc.d remove => defaults => enable|disable <runlevel>" do
- expect_commands(@provider, [
- "/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
- "/usr/sbin/update-rc.d #{service_name} enable 2",
- "/usr/sbin/update-rc.d #{service_name} disable 3",
- ])
- @provider.enable_service
+ context "when modern update-rc.d is detected" do
+ before do
+ expect(@provider).to receive(:use_legacy_update_rc_d?).and_return(false)
+ end
+
+ it "calls update-rc.d remove => defaults => enable|disable <runlevel>" do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} enable 2",
+ "/usr/sbin/update-rc.d #{service_name} disable 3",
+ ])
+ @provider.enable_service
+ end
+ end
+
+ context "when legacy update-rc.d is detected" do
+ before do
+ expect(@provider).to receive(:use_legacy_update_rc_d?).and_return(true)
+ end
+
+ it "calls update-rc.d remove => start|stop <priority> <levels> ." do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} start 20 2 . stop 55 3 . ",
+ ])
+ @provider.disable_service
+ end
end
end
end
describe "disable_service" do
let(:service_name) { @new_resource.service_name }
- context "when the service doesn't set a priority" do
- it "calls update-rc.d remove => defaults => disable" do
- expect_commands(@provider, [
- "/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
- "/usr/sbin/update-rc.d #{service_name} disable",
- ])
- @provider.disable_service
- end
- end
- context "when the service sets a simple priority" do
+ context "when modern update-rc.d is detected" do
before do
- @new_resource.priority(75)
+ expect(@provider).to receive(:use_legacy_update_rc_d?).and_return(false)
end
- it "calls update-rc.d remove => defaults => disable" do
- expect_commands(@provider, [
- "/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
- "/usr/sbin/update-rc.d #{service_name} disable",
- ])
- @provider.disable_service
+ context "when the service doesn't set a priority" do
+ it "calls update-rc.d remove => defaults => disable" do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} disable",
+ ])
+ @provider.disable_service
+ end
+ end
+
+ context "when the service sets a simple priority 75" do
+ before do
+ @new_resource.priority(75)
+ end
+
+ it "ignores priority and calls update-rc.d remove => defaults => disable" do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} disable",
+ ])
+ @provider.disable_service
+ end
+ end
+
+ context "when the service sets complex priorities using Hash" do
+ before do
+ @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
+ end
+
+ it "ignores priority and calls update-rc.d remove => defaults => enable|disable <runlevel>" do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} enable 2",
+ "/usr/sbin/update-rc.d #{service_name} disable 3",
+ ])
+ @provider.disable_service
+ end
end
end
- context "when the service sets complex priorities" do
+ context "when legacy update-rc.d is detected" do
before do
- @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
+ expect(@provider).to receive(:use_legacy_update_rc_d?).and_return(true)
end
- it "calls update-rc.d remove => defaults => enable|disable <runlevel>" do
- expect_commands(@provider, [
- "/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
- "/usr/sbin/update-rc.d #{service_name} enable 2",
- "/usr/sbin/update-rc.d #{service_name} disable 3",
- ])
- @provider.disable_service
+ context "when the service doesn't set a priority" do
+ it "assumes default priority 20 and calls update-rc.d remove => stop 80 2 3 4 5 ." do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d -f #{service_name} stop 80 2 3 4 5 .",
+ ])
+ @provider.disable_service
+ end
+ end
+
+ context "when the service sets a simple priority 75" do
+ before do
+ @new_resource.priority(75)
+ end
+
+ it "reverses priority and calls update-rc.d remove => stop 25 2 3 4 5 ." do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d -f #{service_name} stop #{100 - @new_resource.priority} 2 3 4 5 .",
+ ])
+ @provider.disable_service
+ end
end
+
+ context "when the service sets complex priorities using Hash" do
+ before do
+ @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
+ end
+
+ it "calls update-rc.d remove => start|stop <priority> <levels> ." do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} start 20 2 . stop 55 3 . ",
+ ])
+ @provider.disable_service
+ end
+ end
+ end
+ end
+
+ describe "use_legacy_update_rc_d?" do
+ let(:dpkg_query_cmd) { "dpkg-query -W --showformat '${Version}' sysv-rc" }
+
+ it "is true when svsv-rc package version < 2.88 is installed" do
+ shellout_result = double(stdout: "2.86.ds1-34\n")
+ expect(@provider).to receive(:shell_out!).with(dpkg_query_cmd).and_return(shellout_result)
+ expect(@provider.use_legacy_update_rc_d?).to eq(true)
+ end
+
+ it "is true when sysv-rc is 2.88 and patch level < ('dsf-')42" do
+ shellout_result = double(stdout: "2.88.dsf-41\n")
+ expect(@provider).to receive(:shell_out!).with(dpkg_query_cmd).and_return(shellout_result)
+ expect(@provider.use_legacy_update_rc_d?).to eq(true)
+ end
+
+ it "is false when sysv-rc package version >= 2.88 is installed" do
+ shellout_result = double(stdout: "2.88dsf-59.9\n")
+ expect(@provider).to receive(:shell_out!).with(dpkg_query_cmd).and_return(shellout_result)
+ expect(@provider.use_legacy_update_rc_d?).to eq(false)
+ end
+
+ it "is false when sysv-rc package is not installed" do
+ shellout_result = double(stdout: "\n")
+ expect(@provider).to receive(:shell_out!).with(dpkg_query_cmd).and_return(shellout_result)
+ expect(@provider.use_legacy_update_rc_d?).to eq(false)
end
end
end