diff options
author | Tim Smith <tsmith84@gmail.com> | 2020-08-25 15:06:55 -0700 |
---|---|---|
committer | Tim Smith <tsmith84@gmail.com> | 2020-08-25 15:06:55 -0700 |
commit | ce0826db2f21578dc6f1ec3bd349966dfd68762f (patch) | |
tree | e1333638fc8aa60908447f75a1c20df6a6a06a1c | |
parent | 68cf3453975dccaad52ec76a17f35c6f6d824b5b (diff) | |
download | chef-ce0826db2f21578dc6f1ec3bd349966dfd68762f.tar.gz |
Add initial take at chef_client_launchd
The good: We have a resource to configure chef-client to run as a service on macos
The bad: Launchd will stop the service if a reconfigure is necessary and it will not start back up. This is a known issue and it exists in the chef-client cookbook as well, but we need to solve it by forked off the chef-client process before we restart the launchd agent.
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | lib/chef/resource/chef_client_launchd.rb | 139 | ||||
-rw-r--r-- | lib/chef/resources.rb | 3 | ||||
-rw-r--r-- | spec/unit/resource/chef_client_launchd.rb | 77 |
3 files changed, 218 insertions, 1 deletions
diff --git a/lib/chef/resource/chef_client_launchd.rb b/lib/chef/resource/chef_client_launchd.rb new file mode 100644 index 0000000000..91d7717b3f --- /dev/null +++ b/lib/chef/resource/chef_client_launchd.rb @@ -0,0 +1,139 @@ +# +# Copyright:: Copyright (c) Chef Software Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require_relative "../resource" +require_relative "../dist" +class Chef + class Resource + class ChefClientLaunchd < Chef::Resource + unified_mode true + + provides :chef_client_launchd + + description "Use the **chef_client_launchd** resource to configure the #{Chef::Dist::PRODUCT} to run on a schedule." + introduced "16.5" + examples <<~DOC + **Set the #{Chef::Dist::PRODUCT} to run on a schedule**: + + ```ruby + chef_client_launchd 'Setup the #{Chef::Dist::PRODUCT} to run every 30 minutes' do + interval 30 + action :enable + end + ``` + + **Disable the #{Chef::Dist::PRODUCT} running on a schedule**: + + ```ruby + chef_client_launchd 'Prevent the #{Chef::Dist::PRODUCT} from running on a schedule' do + action :disable + end + ``` + DOC + + property :user, String, + description: "The name of the user that #{Chef::Dist::PRODUCT} runs as.", + default: "root" + + property :working_directory, String, + description: "The working directory to run the #{Chef::Dist::PRODUCT} from.", + default: "/var/root" + + property :interval, Integer, + description: "Time in minutes between #{Chef::Dist::PRODUCT} executions.", + default: 30 + + property :accept_chef_license, [true, false], + description: "Accept the Chef Online Master License and Services Agreement. See <https://www.chef.io/online-master-agreement/>", + default: false + + property :config_directory, String, + description: "The path of the config directory.", + default: Chef::Dist::CONF_DIR + + property :log_directory, String, + description: "The path of the directory to create the log file in.", + default: "/Library/Logs/Chef" + + property :log_file_name, String, + description: "The name of the log file to use.", + default: "client.log" + + property :chef_binary_path, String, + default: "/opt/#{Chef::Dist::DIR_SUFFIX}/bin/#{Chef::Dist::CLIENT}", + description: "The path to the #{Chef::Dist::CLIENT} binary." + + property :daemon_options, Array, + default: lazy { [] }, + description: "An array of options to pass to the #{Chef::Dist::CLIENT} command." + + property :environment, Hash, + default: lazy { {} }, + description: "A Hash containing additional arbitrary environment variables under which the cron job will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`." + + property :nice, Integer, + description: "The process priority to run the #{Chef::Dist::CLIENT} process at.", + default: 0 + + property :low_priority_io, [true, false], + description: "Run the #{Chef::Dist::CLIENT} process with low priority disk IO", + default: true + + action :enable do + unless ::Dir.exist?(new_resource.log_directory) + directory new_resource.log_directory do + owner new_resource.user + mode "0640" + recursive true + end + end + + launchd "com.chef.chef-client" do + username new_resource.user + working_directory new_resource.working_directory + start_interval new_resource.interval * 60 + program new_resource.chef_binary_path + program_arguments all_daemon_options + environment_variables new_resource.environment unless new_resource.environment.empty? + nice new_resource.nice + low_priority_io true + action %i{create enable} + end + end + + action :disable do + service "chef-client" do + service_name "com.chef.chef-client" + action :disable + end + end + + action_class do + # + # Take daemon_options property and append extra daemon options from other properties + # to build the complete set of options we pass to the client + # + # @return [Array] + # + def all_daemon_options + options = new_resource.daemon_options + ["-L", ::File.join(new_resource.log_directory, new_resource.log_file_name), "-c", ::File.join(new_resource.config_directory, "client.rb")] + options.append("--chef-license", "accept") if new_resource.accept_chef_license + options + end + end + end + end +end
\ No newline at end of file diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index a3f23532b4..d9354358ce 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -28,6 +28,7 @@ require_relative "resource/breakpoint" require_relative "resource/build_essential" require_relative "resource/cookbook_file" require_relative "resource/chef_client_cron" +require_relative "resource/chef_client_launchd" require_relative "resource/chef_client_scheduled_task" require_relative "resource/chef_client_systemd_timer" require_relative "resource/chef_client_trusted_certificate" @@ -168,4 +169,4 @@ require_relative "resource/windows_uac" require_relative "resource/windows_workgroup" require_relative "resource/timezone" require_relative "resource/windows_user_privilege" -require_relative "resource/windows_security_policy" +require_relative "resource/windows_security_policy"
\ No newline at end of file diff --git a/spec/unit/resource/chef_client_launchd.rb b/spec/unit/resource/chef_client_launchd.rb new file mode 100644 index 0000000000..6ec7889c79 --- /dev/null +++ b/spec/unit/resource/chef_client_launchd.rb @@ -0,0 +1,77 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::ChefClientLaunchd do + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + let(:resource) { Chef::Resource::ChefClientLaunchd.new("fakey_fakerton", run_context) } + let(:provider) { resource.provider_for_action(:enable) } + + it "sets the default action as :enable" do + expect(resource.action).to eql([:enable]) + 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 + + it "supports :enable and :disable actions" do + expect { resource.action :enable }.not_to raise_error + expect { resource.action :disable }.not_to raise_error + end + + describe "#all_daemon_options" do + it "returns log and config flags if by default" do + expect(provider.all_daemon_options).to eql( + ["-L", "/Library/Logs/Chef/client.log", "-c", "/etc/chef/client.rb"] + ) + end + + it "appends to any passed daemon options" do + resource.daemon_options %w{foo bar} + expect(provider.all_daemon_options).to eql( + ["foo", "bar", "-L", "/Library/Logs/Chef/client.log", "-c", "/etc/chef/client.rb"] + ) + end + + it "adds license acceptance flags if the property is set" do + resource.accept_chef_license true + expect(provider.all_daemon_options).to eql( + ["-L", "/Library/Logs/Chef/client.log", "-c", "/etc/chef/client.rb", "--chef-license", "accept"] + ) + end + + it "uses custom config dir if set" do + resource.config_directory "/etc/some_other_dir" + expect(provider.all_daemon_options).to eql( + ["-L", "/Library/Logs/Chef/client.log", "-c", "/etc/some_other_dir/client.rb"] + ) + end + + it "uses custom log files / paths if set" do + resource.log_file_name "my-client.log" + resource.log_directory "/var/log/my-chef/" + expect(provider.all_daemon_options).to eql( + ["-L", "/var/log/my-chef/my-client.log", "-c", "/etc/chef/client.rb"] + ) + end + end +end |