summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith84@gmail.com>2020-09-03 12:24:15 -0700
committerTim Smith <tsmith84@gmail.com>2020-09-03 12:25:17 -0700
commitea2d90df45816835eeb753aa4ec0d14583f45e4b (patch)
tree7ea5f342a3b4a55ed50fb635e07fab2ff12bbbb6
parentfb4197f19f60740780c35f4ff37adbd493fdb2dc (diff)
downloadchef-ea2d90df45816835eeb753aa4ec0d14583f45e4b.tar.gz
chef_client_systemd_timer: Add the ability to set CPUQuota on the chef-client unit
This allows us to set a quota like 50% on everything handled by the chef-client including all the sub-processes like ohai or inspec. I looked at using systemd's nice functionality since that would give us a unified property for all the *nix processes, but that only applies to the chef-client process itself. This is much more powerful and gives users what user's probably really want which is "chef and all its stuff should not consume all the CPU on my host". Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/linux.rb1
-rw-r--r--lib/chef/resource/chef_client_systemd_timer.rb7
-rw-r--r--spec/unit/resource/chef_client_systemd_timer_spec.rb37
3 files changed, 44 insertions, 1 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
index ba1f5e84f7..95051bcdb6 100644
--- a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
@@ -100,6 +100,7 @@ end
chef_client_systemd_timer "Run chef-client as a systemd timer" do
interval "1hr"
+ cpu_quote 50
only_if { systemd? }
end
diff --git a/lib/chef/resource/chef_client_systemd_timer.rb b/lib/chef/resource/chef_client_systemd_timer.rb
index fa59798009..e050714a4f 100644
--- a/lib/chef/resource/chef_client_systemd_timer.rb
+++ b/lib/chef/resource/chef_client_systemd_timer.rb
@@ -98,6 +98,12 @@ class Chef
description: "A Hash containing additional arbitrary environment variables under which the systemd timer will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`.",
default: lazy { {} }
+ property :cpu_quota, [Integer, String],
+ description: "The systemd CPUQuota to run the #{Chef::Dist::CLIENT} process with. This is a percentage value of the total CPU time available on the system.",
+ introduced: "16.5",
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be an Integer between 1 and 100" => proc { |v| v > 0 && v <= 100 } }
+
action :add do
systemd_unit "#{new_resource.job_name}.service" do
content service_content
@@ -171,6 +177,7 @@ class Chef
}
unit["Service"]["ConditionACPower"] = "true" unless new_resource.run_on_battery
+ unit["Service"]["CPUQuota"] = new_resource.cpu_quota if new_resource.cpu_quota
unit["Service"]["Environment"] = new_resource.environment.collect { |k, v| "\"#{k}=#{v}\"" } unless new_resource.environment.empty?
unit
end
diff --git a/spec/unit/resource/chef_client_systemd_timer_spec.rb b/spec/unit/resource/chef_client_systemd_timer_spec.rb
index 1866060530..c3d69fcae0 100644
--- a/spec/unit/resource/chef_client_systemd_timer_spec.rb
+++ b/spec/unit/resource/chef_client_systemd_timer_spec.rb
@@ -33,6 +33,12 @@ describe Chef::Resource::ChefClientSystemdTimer do
expect(resource.user).to eql("root")
end
+ it "validates the cpu_quota property input" do
+ expect { resource.cpu_quota(0) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.cpu_quota(101) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.cpu_quota(50) }.not_to raise_error
+ end
+
it "builds a default value for chef_binary_path dist values" do
expect(resource.chef_binary_path).to eql("/opt/chef/bin/chef-client")
end
@@ -70,4 +76,33 @@ describe Chef::Resource::ChefClientSystemdTimer do
expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client --chef-license accept -c #{root_path}")
end
end
-end
+
+ describe "#service_content" do
+ it "does not set ConditionACPower if run_on_battery property is set to true (the default)" do
+ expect(provider.service_content["Service"]).not_to have_key("ConditionACPower")
+ end
+
+ it "sets ConditionACPower if run_on_battery property is set to false" do
+ resource.run_on_battery false
+ expect(provider.service_content["Service"]["ConditionACPower"]).to eq("true")
+ end
+
+ it "does not set Environment if environment property is empty" do
+ expect(provider.service_content["Service"]).not_to have_key("Environment")
+ end
+
+ it "sets Environment if environment property is set" do
+ resource.environment({ "foo" => "bar" })
+ expect(provider.service_content["Service"]["Environment"]).to eq(["\"foo=bar\""])
+ end
+
+ it "does not set CPUQuota if cpu_quota property is not set" do
+ expect(provider.service_content["Service"]).not_to have_key("CPUQuota")
+ end
+
+ it "sets CPUQuota if cpu_quota property is set" do
+ resource.cpu_quota 50
+ expect(provider.service_content["Service"]["CPUQuota"]).to eq(50)
+ end
+ end
+end \ No newline at end of file