summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith84@gmail.com>2020-08-25 15:06:55 -0700
committerTim Smith <tsmith84@gmail.com>2020-08-25 15:06:55 -0700
commitce0826db2f21578dc6f1ec3bd349966dfd68762f (patch)
treee1333638fc8aa60908447f75a1c20df6a6a06a1c
parent68cf3453975dccaad52ec76a17f35c6f6d824b5b (diff)
downloadchef-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.rb139
-rw-r--r--lib/chef/resources.rb3
-rw-r--r--spec/unit/resource/chef_client_launchd.rb77
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