summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2019-07-23 09:52:44 -0700
committerGitHub <noreply@github.com>2019-07-23 09:52:44 -0700
commitbbbb5910f659b6774d004f0425a6594104c65565 (patch)
tree7bb963f6e8f89b5f23e34e69ec246a2efa4a10a9
parent911644351fd9c003630b9c7e8513789729566899 (diff)
parent16a638197c978721b1a98a51acdad06165ff3ffe (diff)
downloadchef-bbbb5910f659b6774d004f0425a6594104c65565.tar.gz
Merge pull request #8744 from chef/lcg/application-base-class
Move chef-client and chef-solo shared code into a base class and remove duplication and skew
-rw-r--r--lib/chef/application/base.rb440
-rw-r--r--lib/chef/application/client.rb395
-rw-r--r--lib/chef/application/solo.rb267
3 files changed, 456 insertions, 646 deletions
diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb
new file mode 100644
index 0000000000..fea3d844ce
--- /dev/null
+++ b/lib/chef/application/base.rb
@@ -0,0 +1,440 @@
+#
+# Copyright:: Copyright 2008-2019, 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_relative "../application"
+require_relative "../client"
+require_relative "../log"
+require_relative "../config"
+require_relative "../mixin/shell_out"
+require_relative "../config_fetcher"
+require_relative "../dist"
+require_relative "../daemon"
+require "chef-config/mixin/dot_d"
+require "license_acceptance/cli_flags/mixlib_cli"
+require "mixlib/archive" unless defined?(Mixlib::Archive)
+
+# This is a temporary class being used as a part of an effort to reduce duplication
+# between Chef::Application::Client and Chef::Application::Solo.
+#
+# If you are looking to make edits to the Client/Solo behavior please make changes here.
+#
+# If you are looking to reference or subclass this class, use Chef::Application::Client
+# instead. This base class will be removed once the work is complete and external code
+# will break.
+#
+# @deprecated use Chef::Application::Client instead, this will be removed in Chef-16
+#
+class Chef::Application::Base < Chef::Application
+ include Chef::Mixin::ShellOut
+ include ChefConfig::Mixin::DotD
+ include LicenseAcceptance::CLIFlags::MixlibCLI
+
+ # Mimic self_pipe sleep from Unicorn to capture signals safely
+ SELF_PIPE = [] # rubocop:disable Style/MutableConstant
+
+ option :config_option,
+ long: "--config-option OPTION=VALUE",
+ description: "Override a single configuration option.",
+ proc: lambda { |option, existing|
+ (existing ||= []) << option
+ existing
+ }
+
+ option :once,
+ long: "--once",
+ description: "Cancel any interval or splay options, run #{Chef::Dist::PRODUCT} once and exit.",
+ boolean: true
+
+ option :formatter,
+ short: "-F FORMATTER",
+ long: "--format FORMATTER",
+ description: "The output format to use.",
+ proc: lambda { |format| Chef::Config.add_formatter(format) }
+
+ option :force_logger,
+ long: "--force-logger",
+ description: "Use logger output instead of formatter output.",
+ boolean: true,
+ default: false
+
+ option :force_formatter,
+ long: "--force-formatter",
+ description: "Use formatter output instead of logger output.",
+ boolean: true,
+ default: false
+
+ option :profile_ruby,
+ long: "--[no-]profile-ruby",
+ description: "Dump complete Ruby call graph stack of entire #{Chef::Dist::PRODUCT} run (expert only).",
+ boolean: true,
+ default: false
+
+ option :color,
+ long: "--[no-]color",
+ boolean: true,
+ default: true,
+ description: "Use colored output, defaults to enabled."
+
+ option :log_level,
+ short: "-l LEVEL",
+ long: "--log_level LEVEL",
+ description: "Set the log level (auto, trace, debug, info, warn, error, fatal).",
+ proc: lambda { |l| l.to_sym }
+
+ option :log_location,
+ short: "-L LOGLOCATION",
+ long: "--logfile LOGLOCATION",
+ description: "Set the log file location, defaults to STDOUT - recommended for daemonizing.",
+ proc: nil
+
+ option :help,
+ short: "-h",
+ long: "--help",
+ description: "Show this help message.",
+ on: :tail,
+ boolean: true,
+ show_options: true,
+ exit: 0
+
+ option :user,
+ short: "-u USER",
+ long: "--user USER",
+ description: "User to set privilege to.",
+ proc: nil
+
+ option :group,
+ short: "-g GROUP",
+ long: "--group GROUP",
+ description: "Group to set privilege to.",
+ proc: nil
+
+ option :lockfile,
+ long: "--lockfile LOCKFILE",
+ description: "Set the lockfile location. Prevents multiple client processes from converging at the same time.",
+ proc: nil
+
+ option :interval,
+ short: "-i SECONDS",
+ long: "--interval SECONDS",
+ description: "Run #{Chef::Dist::PRODUCT} periodically, in seconds.",
+ proc: lambda { |s| s.to_i }
+
+ option :json_attribs,
+ short: "-j JSON_ATTRIBS",
+ long: "--json-attributes JSON_ATTRIBS",
+ description: "Load attributes from a JSON file or URL.",
+ proc: nil
+
+ option :node_name,
+ short: "-N NODE_NAME",
+ long: "--node-name NODE_NAME",
+ description: "The node name for this client.",
+ proc: nil
+
+ option :splay,
+ short: "-s SECONDS",
+ long: "--splay SECONDS",
+ description: "The splay time for running at intervals, in seconds.",
+ proc: lambda { |s| s.to_i }
+
+ option :environment,
+ short: "-E ENVIRONMENT",
+ long: "--environment ENVIRONMENT",
+ description: "Set the #{Chef::Dist::PRODUCT} environment on the node."
+
+ option :client_fork,
+ short: "-f",
+ long: "--[no-]fork",
+ description: "Fork #{Chef::Dist::PRODUCT} process."
+
+ option :why_run,
+ short: "-W",
+ long: "--why-run",
+ description: "Enable whyrun mode.",
+ boolean: true
+
+ option :override_runlist,
+ short: "-o RunlistItem,RunlistItem...",
+ long: "--override-runlist RunlistItem,RunlistItem...",
+ description: "Replace current run list with specified items for a single run.",
+ proc: lambda { |items|
+ items = items.split(",")
+ items.compact.map do |item|
+ Chef::RunList::RunListItem.new(item)
+ end
+ }
+
+ option :run_lock_timeout,
+ long: "--run-lock-timeout SECONDS",
+ description: "Set maximum duration to wait for another client run to finish, default is indefinitely.",
+ proc: lambda { |s| s.to_i }
+
+ option :version,
+ short: "-v",
+ long: "--version",
+ description: "Show #{Chef::Dist::PRODUCT} version.",
+ boolean: true,
+ proc: lambda { |v| puts "#{Chef::Dist::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
+
+ option :minimal_ohai,
+ long: "--minimal-ohai",
+ description: "Only run the bare minimum Ohai plugins #{Chef::Dist::PRODUCT} needs to function.",
+ boolean: true
+
+ option :delete_entire_chef_repo,
+ long: "--delete-entire-chef-repo",
+ description: "DANGEROUS: does what it says, only useful with --recipe-url.",
+ boolean: true
+
+ option :ez,
+ long: "--ez",
+ description: "A memorial for Ezra Zygmuntowicz.",
+ boolean: true
+
+ option :target,
+ short: "-t TARGET",
+ long: "--target TARGET",
+ description: "Target #{Chef::Dist::PRODUCT} against a remote system or device",
+ proc: lambda { |target|
+ Chef::Log.warn "-- EXPERIMENTAL -- Target mode activated, resources and dsl may change without warning -- EXPERIMENTAL --"
+ target
+ }
+
+ option :disable_config,
+ long: "--disable-config",
+ description: "Refuse to load a config file and use defaults. This is for development and not a stable API.",
+ boolean: true
+
+ if Chef::Platform.windows?
+ option :fatal_windows_admin_check,
+ short: "-A",
+ long: "--fatal-windows-admin-check",
+ description: "Fail the run when #{Chef::Dist::CLIENT} doesn't have administrator privileges on Windows.",
+ boolean: true
+ end
+
+ option :fips,
+ long: "--[no-]fips",
+ description: "Enable FIPS mode.",
+ boolean: true
+
+ option :solo_legacy_mode,
+ long: "--legacy-mode",
+ description: "Run in legacy mode.",
+ boolean: true
+
+ option :chef_server_url,
+ short: "-S CHEFSERVERURL",
+ long: "--server CHEFSERVERURL",
+ description: "The #{Chef::Dist::SERVER_PRODUCT} URL.",
+ proc: nil
+
+ option :validation_key,
+ short: "-K KEY_FILE",
+ long: "--validation_key KEY_FILE",
+ description: "Set the validation key file location, used for registering new clients.",
+ proc: nil
+
+ option :client_key,
+ short: "-k KEY_FILE",
+ long: "--client_key KEY_FILE",
+ description: "Set the client key file location.",
+ proc: nil
+
+ option :enable_reporting,
+ short: "-R",
+ long: "--enable-reporting",
+ description: "(#{Chef::Dist::CLIENT} only) reporting data collection for runs.",
+ boolean: true
+
+ option :local_mode,
+ short: "-z",
+ long: "--local-mode",
+ description: "Point at local repository.",
+ boolean: true
+
+ option :chef_zero_host,
+ long: "--chef-zero-host HOST",
+ description: "Host to start #{Chef::Dist::ZERO} on."
+
+ option :chef_zero_port,
+ long: "--chef-zero-port PORT",
+ description: "Port (or port range) to start #{Chef::Dist::ZERO} on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
+
+ option :listen,
+ long: "--[no-]listen",
+ description: "Whether a local mode (-z) server binds to a port.",
+ boolean: false
+
+ option :skip_cookbook_sync,
+ long: "--[no-]skip-cookbook-sync",
+ description: "(#{Chef::Dist::CLIENT} only) Use cached cookbooks without overwriting local differences from the #{Chef::Dist::SERVER_PRODUCT}.",
+ boolean: false
+
+ option :named_run_list,
+ short: "-n NAMED_RUN_LIST",
+ long: "--named-run-list NAMED_RUN_LIST",
+ description: "Use a policyfile's named run list instead of the default run list."
+
+ IMMEDIATE_RUN_SIGNAL = "1".freeze
+ RECONFIGURE_SIGNAL = "H".freeze
+
+ attr_reader :chef_client_json
+
+ def setup_application
+ Chef::Daemon.change_privilege
+ end
+
+ def setup_signal_handlers
+ super
+
+ unless Chef::Platform.windows?
+ SELF_PIPE.replace IO.pipe
+
+ trap("USR1") do
+ Chef::Log.info("SIGUSR1 received, will run now or after the current run")
+ SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select
+ end
+
+ # Override the trap setup in the parent so we can avoid running reconfigure during a run
+ trap("HUP") do
+ Chef::Log.info("SIGHUP received, will reconfigure now or after the current run")
+ SELF_PIPE[1].putc(RECONFIGURE_SIGNAL) # wakeup master process from select
+ end
+ end
+ end
+
+ # Run the chef client, optionally daemonizing or looping at intervals.
+ def run_application
+ if Chef::Config[:version]
+ puts "#{Chef::Dist::PRODUCT} version: #{::Chef::VERSION}"
+ end
+
+ if !Chef::Config[:client_fork] || Chef::Config[:once]
+ begin
+ # run immediately without interval sleep, or splay
+ run_chef_client(Chef::Config[:specific_recipes])
+ rescue SystemExit
+ raise
+ rescue Exception => e
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
+ end
+ else
+ interval_run_chef_client
+ end
+ end
+
+ private
+
+ def unforked_interval_error_message
+ "Unforked #{Chef::Dist::PRODUCT} interval runs are disabled by default." +
+ "\nConfiguration settings:" +
+ ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
+ "\nEnable #{Chef::Dist::PRODUCT} interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
+ end
+
+ def fetch_recipe_tarball(url, path)
+ Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
+ if File.exist?(url)
+ FileUtils.cp(url, path)
+ elsif url =~ URI.regexp
+ File.open(path, "wb") do |f|
+ open(url) do |r|
+ f.write(r.read)
+ end
+ end
+ else
+ Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL nor a path to a file that exists on disk." +
+ "Please confirm the location of the tarball and try again."
+ end
+ end
+
+ def interval_run_chef_client
+ if Chef::Config[:daemonize]
+ Chef::Daemon.daemonize(Chef::Dist::PRODUCT)
+
+ # Start first daemonized run after configured number of seconds
+ if Chef::Config[:daemonize].is_a?(Integer)
+ sleep_then_run_chef_client(Chef::Config[:daemonize])
+ end
+ end
+
+ loop do
+ sleep_then_run_chef_client(time_to_sleep)
+ Chef::Application.exit!("Exiting", 0) unless Chef::Config[:interval]
+ end
+ end
+
+ def sleep_then_run_chef_client(sleep_sec)
+ Chef::Log.trace("Sleeping for #{sleep_sec} seconds")
+
+ # interval_sleep will return early if we received a signal (unless on windows)
+ interval_sleep(sleep_sec)
+
+ run_chef_client(Chef::Config[:specific_recipes])
+
+ reconfigure
+ rescue SystemExit => e
+ raise
+ rescue Exception => e
+ if Chef::Config[:interval]
+ Chef::Log.error("#{e.class}: #{e}")
+ Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
+ retry
+ end
+
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
+ end
+
+ def time_to_sleep
+ duration = 0
+ duration += rand(Chef::Config[:splay]) if Chef::Config[:splay]
+ duration += Chef::Config[:interval] if Chef::Config[:interval]
+ duration
+ end
+
+ # sleep and handle queued signals
+ def interval_sleep(sec)
+ unless SELF_PIPE.empty?
+ # mimic sleep with a timeout on IO.select, listening for signals setup in #setup_signal_handlers
+ return unless IO.select([ SELF_PIPE[0] ], nil, nil, sec)
+
+ signal = SELF_PIPE[0].getc.chr
+
+ return if signal == IMMEDIATE_RUN_SIGNAL # be explicit about this behavior
+
+ # we need to sleep again after reconfigure to avoid stampeding when logrotate runs out of cron
+ if signal == RECONFIGURE_SIGNAL
+ reconfigure
+ interval_sleep(sec)
+ end
+ else
+ sleep(sec)
+ end
+ end
+
+ def for_ezra
+ puts <<~EOH
+ For Ezra Zygmuntowicz:
+ The man who brought you Chef Solo
+ Early contributor to Chef
+ Kind hearted open source advocate
+ Rest in peace, Ezra.
+ EOH
+ end
+
+end
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 1e1b76de15..0fc5ca7711 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -17,105 +17,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require_relative "../application"
-require_relative "../client"
-require_relative "../config"
-require_relative "../daemon"
-require_relative "../log"
-require_relative "../config_fetcher"
+require_relative "base"
require_relative "../handler/error_report"
require_relative "../workstation_config_loader"
-require_relative "../mixin/shell_out"
-require "chef-config/mixin/dot_d"
-require "mixlib/archive" unless defined?(Mixlib::Archive)
require "uri" unless defined?(URI)
-require_relative "../dist"
-require "license_acceptance/cli_flags/mixlib_cli"
-class Chef::Application::Client < Chef::Application
- include Chef::Mixin::ShellOut
- include ChefConfig::Mixin::DotD
- include LicenseAcceptance::CLIFlags::MixlibCLI
-
- # Mimic self_pipe sleep from Unicorn to capture signals safely
- SELF_PIPE = [] # rubocop:disable Style/MutableConstant
+# DO NOT MAKE EDITS, see Chef::Application::Base
+#
+# External code may call / subclass or make references to this class.
+#
+class Chef::Application::Client < Chef::Application::Base
option :config_file,
short: "-c CONFIG",
long: "--config CONFIG",
description: "The configuration file to use."
- option :config_option,
- long: "--config-option OPTION=VALUE",
- description: "Override a single configuration option.",
- proc: lambda { |option, existing|
- (existing ||= []) << option
- existing
- }
-
- option :formatter,
- short: "-F FORMATTER",
- long: "--format FORMATTER",
- description: "The output format to use.",
- proc: lambda { |format| Chef::Config.add_formatter(format) }
-
- option :force_logger,
- long: "--force-logger",
- description: "Use logger output instead of formatter output.",
- boolean: true,
- default: false
-
- option :force_formatter,
- long: "--force-formatter",
- description: "Use formatter output instead of logger output.",
- boolean: true,
- default: false
-
- option :profile_ruby,
- long: "--[no-]profile-ruby",
- description: "Dump complete Ruby call graph stack of entire #{Chef::Dist::PRODUCT} run (expert only).",
- boolean: true,
- default: false
-
- option :color,
- long: "--[no-]color",
- boolean: true,
- default: true,
- description: "Use colored output, defaults to enabled."
-
- option :log_level,
- short: "-l LEVEL",
- long: "--log_level LEVEL",
- description: "Set the log level (auto, trace, debug, info, warn, error, fatal).",
- proc: lambda { |l| l.to_sym }
-
- option :log_location,
- short: "-L LOGLOCATION",
- long: "--logfile LOGLOCATION",
- description: "Set the log file location, defaults to STDOUT - recommended for daemonizing.",
- proc: nil
-
- option :help,
- short: "-h",
- long: "--help",
- description: "Show this help message.",
- on: :tail,
- boolean: true,
- show_options: true,
- exit: 0
-
- option :user,
- short: "-u USER",
- long: "--user USER",
- description: "User to set privilege to.",
- proc: nil
-
- option :group,
- short: "-g GROUP",
- long: "--group GROUP",
- description: "Group to set privilege to.",
- proc: nil
-
unless Chef::Platform.windows?
option :daemonize,
short: "-d [WAIT]",
@@ -131,87 +48,6 @@ class Chef::Application::Client < Chef::Application
description: "Set the PID file location, for the #{Chef::Dist::CLIENT} daemon process. Defaults to /tmp/chef-client.pid.",
proc: nil
- option :lockfile,
- long: "--lockfile LOCKFILE",
- description: "Set the lockfile location. Prevents multiple client processes from converging at the same time.",
- proc: nil
-
- option :interval,
- short: "-i SECONDS",
- long: "--interval SECONDS",
- description: "Run #{Chef::Dist::CLIENT} periodically, in seconds.",
- proc: lambda { |s| s.to_i }
-
- option :once,
- long: "--once",
- description: "Cancel any interval or splay options, run #{Chef::Dist::CLIENT} once and exit.",
- boolean: true
-
- option :json_attribs,
- short: "-j JSON_ATTRIBS",
- long: "--json-attributes JSON_ATTRIBS",
- description: "Load attributes from a JSON file or URL.",
- proc: nil
-
- option :node_name,
- short: "-N NODE_NAME",
- long: "--node-name NODE_NAME",
- description: "The node name for this client.",
- proc: nil
-
- option :splay,
- short: "-s SECONDS",
- long: "--splay SECONDS",
- description: "The splay time for running at intervals, in seconds.",
- proc: lambda { |s| s.to_i }
-
- option :chef_server_url,
- short: "-S CHEFSERVERURL",
- long: "--server CHEFSERVERURL",
- description: "The #{Chef::Dist::SERVER_PRODUCT} URL.",
- proc: nil
-
- option :validation_key,
- short: "-K KEY_FILE",
- long: "--validation_key KEY_FILE",
- description: "Set the validation key file location, used for registering new clients.",
- proc: nil
-
- option :client_key,
- short: "-k KEY_FILE",
- long: "--client_key KEY_FILE",
- description: "Set the client key file location.",
- proc: nil
-
- option :named_run_list,
- short: "-n NAMED_RUN_LIST",
- long: "--named-run-list NAMED_RUN_LIST",
- description: "Use a policyfile's named run list instead of the default run list."
-
- option :environment,
- short: "-E ENVIRONMENT",
- long: "--environment ENVIRONMENT",
- description: "Set the #{Chef::Dist::PRODUCT} environment on the node."
-
- option :version,
- short: "-v",
- long: "--version",
- description: "Show #{Chef::Dist::PRODUCT} version.",
- boolean: true,
- proc: lambda { |v| puts "#{Chef::Dist::PRODUCT}: #{::Chef::VERSION}" },
- exit: 0
-
- option :override_runlist,
- short: "-o RunlistItem,RunlistItem...",
- long: "--override-runlist RunlistItem,RunlistItem...",
- description: "Replace current run list with specified items for a single run.",
- proc: lambda { |items|
- items = items.split(",")
- items.compact.map do |item|
- Chef::RunList::RunListItem.new(item)
- end
- }
-
option :runlist,
short: "-r RunlistItem,RunlistItem...",
long: "--runlist RunlistItem,RunlistItem...",
@@ -223,98 +59,10 @@ class Chef::Application::Client < Chef::Application
end
}
- option :why_run,
- short: "-W",
- long: "--why-run",
- description: "Enable whyrun mode.",
- boolean: true
-
- option :client_fork,
- short: "-f",
- long: "--[no-]fork",
- description: "Fork #{Chef::Dist::CLIENT} process."
-
option :recipe_url,
long: "--recipe-url=RECIPE_URL",
description: "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode."
- option :enable_reporting,
- short: "-R",
- long: "--enable-reporting",
- description: "Enable reporting data collection for #{Chef::Dist::PRODUCT} runs.",
- boolean: true
-
- option :local_mode,
- short: "-z",
- long: "--local-mode",
- description: "Point #{Chef::Dist::CLIENT} at local repository.",
- boolean: true
-
- option :chef_zero_host,
- long: "--chef-zero-host HOST",
- description: "Host to start #{Chef::Dist::ZERO} on."
-
- option :chef_zero_port,
- long: "--chef-zero-port PORT",
- description: "Port (or port range) to start #{Chef::Dist::ZERO} on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
-
- option :disable_config,
- long: "--disable-config",
- description: "Refuse to load a config file and use defaults. This is for development and not a stable API.",
- boolean: true
-
- option :run_lock_timeout,
- long: "--run-lock-timeout SECONDS",
- description: "Set maximum duration to wait for another client run to finish, default is indefinitely.",
- proc: lambda { |s| s.to_i }
-
- if Chef::Platform.windows?
- option :fatal_windows_admin_check,
- short: "-A",
- long: "--fatal-windows-admin-check",
- description: "Fail the run when #{Chef::Dist::CLIENT} doesn't have administrator privileges on Windows.",
- boolean: true
- end
-
- option :minimal_ohai,
- long: "--minimal-ohai",
- description: "Only run the bare minimum Ohai plugins #{Chef::Dist::PRODUCT} needs to function.",
- boolean: true
-
- option :listen,
- long: "--[no-]listen",
- description: "Whether a local mode (-z) server binds to a port.",
- boolean: false
-
- option :fips,
- long: "--[no-]fips",
- description: "Enable FIPS mode.",
- boolean: true
-
- option :delete_entire_chef_repo,
- long: "--delete-entire-chef-repo",
- description: "DANGEROUS: does what it says, only useful with --recipe-url.",
- boolean: true
-
- option :skip_cookbook_sync,
- long: "--[no-]skip-cookbook-sync",
- description: "Use cached cookbooks without overwriting local differences from the #{Chef::Dist::SERVER_PRODUCT}.",
- boolean: false
-
- option :target,
- short: "-t TARGET",
- long: "--target TARGET",
- description: "Target #{Chef::Dist::PRODUCT} against a remote system or device",
- proc: lambda { |target|
- Chef::Log.warn "-- EXPERIMENTAL -- Target mode activated, resources and dsl may change without warning -- EXPERIMENTAL --"
- target
- }
-
- IMMEDIATE_RUN_SIGNAL = "1".freeze
- RECONFIGURE_SIGNAL = "H".freeze
-
- attr_reader :chef_client_json
-
# Reconfigure the chef client
# Re-open the JSON attributes and load them into the node
def reconfigure
@@ -412,135 +160,4 @@ class Chef::Application::Client < Chef::Application
Ohai::Log.use_log_devices( Chef::Log )
end
- def setup_application
- Chef::Daemon.change_privilege
- end
-
- def setup_signal_handlers
- super
-
- unless Chef::Platform.windows?
- SELF_PIPE.replace IO.pipe
-
- trap("USR1") do
- Chef::Log.info("SIGUSR1 received, will run now or after the current run")
- SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select
- end
-
- # Override the trap setup in the parent so we can avoid running reconfigure during a run
- trap("HUP") do
- Chef::Log.info("SIGHUP received, will reconfigure now or after the current run")
- SELF_PIPE[1].putc(RECONFIGURE_SIGNAL) # wakeup master process from select
- end
- end
- end
-
- # Run the chef client, optionally daemonizing or looping at intervals.
- def run_application
- if Chef::Config[:version]
- puts "#{Chef::Dist::PRODUCT} version: #{::Chef::VERSION}"
- end
-
- if !Chef::Config[:client_fork] || Chef::Config[:once]
- begin
- # run immediately without interval sleep, or splay
- run_chef_client(Chef::Config[:specific_recipes])
- rescue SystemExit
- raise
- rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
- else
- interval_run_chef_client
- end
- end
-
- private
-
- def interval_run_chef_client
- if Chef::Config[:daemonize]
- Chef::Daemon.daemonize(Chef::Dist::CLIENT)
-
- # Start first daemonized run after configured number of seconds
- if Chef::Config[:daemonize].is_a?(Integer)
- sleep_then_run_chef_client(Chef::Config[:daemonize])
- end
- end
-
- loop do
- sleep_then_run_chef_client(time_to_sleep)
- Chef::Application.exit!("Exiting", 0) unless Chef::Config[:interval]
- end
- end
-
- def sleep_then_run_chef_client(sleep_sec)
- Chef::Log.trace("Sleeping for #{sleep_sec} seconds")
-
- # interval_sleep will return early if we received a signal (unless on windows)
- interval_sleep(sleep_sec)
-
- run_chef_client(Chef::Config[:specific_recipes])
-
- reconfigure
- rescue SystemExit => e
- raise
- rescue Exception => e
- if Chef::Config[:interval]
- Chef::Log.error("#{e.class}: #{e}")
- Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
- retry
- end
-
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
-
- def time_to_sleep
- duration = 0
- duration += rand(Chef::Config[:splay]) if Chef::Config[:splay]
- duration += Chef::Config[:interval] if Chef::Config[:interval]
- duration
- end
-
- # sleep and handle queued signals
- def interval_sleep(sec)
- unless SELF_PIPE.empty?
- # mimic sleep with a timeout on IO.select, listening for signals setup in #setup_signal_handlers
- return unless IO.select([ SELF_PIPE[0] ], nil, nil, sec)
-
- signal = SELF_PIPE[0].getc.chr
-
- return if signal == IMMEDIATE_RUN_SIGNAL # be explicit about this behavior
-
- # we need to sleep again after reconfigure to avoid stampeding when logrotate runs out of cron
- if signal == RECONFIGURE_SIGNAL
- reconfigure
- interval_sleep(sec)
- end
- else
- sleep(sec)
- end
- end
-
- def unforked_interval_error_message
- "Unforked #{Chef::Dist::CLIENT} interval runs are disabled by default." +
- "\nConfiguration settings:" +
- ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
- "\nEnable #{Chef::Dist::CLIENT} interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- end
-
- def fetch_recipe_tarball(url, path)
- Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
- if File.exist?(url)
- FileUtils.cp(url, path)
- elsif url =~ URI.regexp
- File.open(path, "wb") do |f|
- open(url) do |r|
- f.write(r.read)
- end
- end
- else
- Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL nor a path to a file that exists on disk." +
- "Please confirm the location of the tarball and try again."
- end
- end
end
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index 29ba1971e1..bce95c2841 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -16,26 +16,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require_relative "base"
require_relative "../../chef"
-require_relative "../application"
require_relative "client"
-require_relative "../client"
-require_relative "../config"
-require_relative "../daemon"
-require_relative "../log"
-require_relative "../config_fetcher"
require "fileutils" unless defined?(FileUtils)
-require_relative "../mixin/shell_out"
require "pathname" unless defined?(Pathname)
-require "chef-config/mixin/dot_d"
-require "mixlib/archive" unless defined?(Mixlib::Archive)
-require_relative "../dist"
-require "license_acceptance/cli_flags/mixlib_cli"
-class Chef::Application::Solo < Chef::Application
- include Chef::Mixin::ShellOut
- include ChefConfig::Mixin::DotD
- include LicenseAcceptance::CLIFlags::MixlibCLI
+# DO NOT MAKE EDITS, see Chef::Application::Base
+#
+# Do not reference this class it will be removed in Chef-16
+#
+# @deprecated use Chef::Application::Client instead, this will be removed in Chef-16
+#
+class Chef::Application::Solo < Chef::Application::Base
option :config_file,
short: "-c CONFIG",
@@ -43,77 +36,6 @@ class Chef::Application::Solo < Chef::Application
default: Chef::Config.platform_specific_path("#{Chef::Dist::CONF_DIR}/solo.rb"),
description: "The configuration file to use."
- option :config_option,
- long: "--config-option OPTION=VALUE",
- description: "Override a single configuration option.",
- proc: lambda { |option, existing|
- (existing ||= []) << option
- existing
- }
-
- option :formatter,
- short: "-F FORMATTER",
- long: "--format FORMATTER",
- description: "The output format to use.",
- proc: lambda { |format| Chef::Config.add_formatter(format) }
-
- option :force_logger,
- long: "--force-logger",
- description: "Use logger output instead of formatter output.",
- boolean: true,
- default: false
-
- option :force_formatter,
- long: "--force-formatter",
- description: "Use formatter output instead of logger output.",
- boolean: true,
- default: false
-
- option :profile_ruby,
- long: "--[no-]profile-ruby",
- description: "Dump complete Ruby call graph stack of entire #{Chef::Dist::PRODUCT} run (expert only).",
- boolean: true,
- default: false
-
- option :color,
- long: "--[no-]color",
- boolean: true,
- default: true,
- description: "Use colored output, defaults to enabled."
-
- option :log_level,
- short: "-l LEVEL",
- long: "--log_level LEVEL",
- description: "Set the log level (auto, trace, debug, info, warn, error, fatal).",
- proc: lambda { |l| l.to_sym }
-
- option :log_location,
- short: "-L LOGLOCATION",
- long: "--logfile LOGLOCATION",
- description: "Set the log file location, defaults to STDOUT - recommended for daemonizing.",
- proc: nil
-
- option :help,
- short: "-h",
- long: "--help",
- description: "Show this help message.",
- on: :tail,
- boolean: true,
- show_options: true,
- exit: 0
-
- option :user,
- short: "-u USER",
- long: "--user USER",
- description: "User to set privilege to.",
- proc: nil
-
- option :group,
- short: "-g GROUP",
- long: "--group GROUP",
- description: "Group to set privilege to.",
- proc: nil
-
unless Chef::Platform.windows?
option :daemonize,
short: "-d",
@@ -122,102 +44,11 @@ class Chef::Application::Solo < Chef::Application
proc: lambda { |p| true }
end
- option :lockfile,
- long: "--lockfile LOCKFILE",
- description: "Set the lockfile location. Prevents multiple #{Chef::Dist::SOLO} processes from converging at the same time.",
- proc: nil
-
- option :interval,
- short: "-i SECONDS",
- long: "--interval SECONDS",
- description: "Run #{Chef::Dist::CLIENT} periodically, in seconds.",
- proc: lambda { |s| s.to_i }
-
- option :json_attribs,
- short: "-j JSON_ATTRIBS",
- long: "--json-attributes JSON_ATTRIBS",
- description: "Load attributes from a JSON file or URL.",
- proc: nil
-
- option :node_name,
- short: "-N NODE_NAME",
- long: "--node-name NODE_NAME",
- description: "The node name for this client.",
- proc: nil
-
- option :splay,
- short: "-s SECONDS",
- long: "--splay SECONDS",
- description: "The splay time for running at intervals, in seconds.",
- proc: lambda { |s| s.to_i }
-
option :recipe_url,
short: "-r RECIPE_URL",
long: "--recipe-url RECIPE_URL",
description: "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache."
- option :version,
- short: "-v",
- long: "--version",
- description: "Show #{Chef::Dist::PRODUCT} version.",
- boolean: true,
- proc: lambda { |v| puts "#{Chef::Dist::PRODUCT}: #{::Chef::VERSION}" },
- exit: 0
-
- option :override_runlist,
- short: "-o RunlistItem,RunlistItem...",
- long: "--override-runlist RunlistItem,RunlistItem...",
- description: "Replace current run list with specified items for a single run.",
- proc: lambda { |items|
- items = items.split(",")
- items.compact.map do |item|
- Chef::RunList::RunListItem.new(item)
- end
- }
-
- option :client_fork,
- short: "-f",
- long: "--[no-]fork",
- description: "Fork #{Chef::Dist::CLIENT} process."
-
- option :why_run,
- short: "-W",
- long: "--why-run",
- description: "Enable whyrun mode.",
- boolean: true
-
- option :ez,
- long: "--ez",
- description: "A memorial for Ezra Zygmuntowicz.",
- boolean: true
-
- option :environment,
- short: "-E ENVIRONMENT",
- long: "--environment ENVIRONMENT",
- description: "Set the #{Chef::Dist::PRODUCT} environment on the node."
-
- option :run_lock_timeout,
- long: "--run-lock-timeout SECONDS",
- description: "Set maximum duration to wait for another client run to finish, default is indefinitely.",
- proc: lambda { |s| s.to_i }
-
- option :minimal_ohai,
- long: "--minimal-ohai",
- description: "Only run the bare minimum Ohai plugins #{Chef::Dist::PRODUCT} needs to function.",
- boolean: true
-
- option :delete_entire_chef_repo,
- long: "--delete-entire-chef-repo",
- description: "DANGEROUS: does what it says, only useful with --recipe-url.",
- boolean: true
-
- option :solo_legacy_mode,
- long: "--legacy-mode",
- description: "Run #{Chef::Dist::SOLO} in legacy mode.",
- boolean: true
-
- attr_reader :chef_client_json
-
# Get this party started
def run(enforce_license: false)
setup_signal_handlers
@@ -239,6 +70,8 @@ class Chef::Application::Solo < Chef::Application
set_specific_recipes
+ Chef::Config[:fips] = config[:fips] if config.key? :fips
+
Chef::Config[:solo] = true
if !Chef::Config[:solo_legacy_mode]
@@ -294,84 +127,4 @@ class Chef::Application::Solo < Chef::Application
end
end
- def setup_application
- Chef::Daemon.change_privilege
- end
-
- def run_application
- if !Chef::Config[:client_fork] || Chef::Config[:once]
- begin
- # run immediately without interval sleep, or splay
- run_chef_client(Chef::Config[:specific_recipes])
- rescue SystemExit
- raise
- rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
- else
- interval_run_chef_client
- end
- end
-
- private
-
- def for_ezra
- puts <<~EOH
- For Ezra Zygmuntowicz:
- The man who brought you Chef Solo
- Early contributor to Chef
- Kind hearted open source advocate
- Rest in peace, Ezra.
- EOH
- end
-
- def interval_run_chef_client
- if Chef::Config[:daemonize]
- Chef::Daemon.daemonize("#{Chef::Dist::CLIENT}")
- end
-
- loop do
- begin
-
- sleep_sec = 0
- sleep_sec += rand(Chef::Config[:splay]) if Chef::Config[:splay]
- sleep_sec += Chef::Config[:interval] if Chef::Config[:interval]
- if sleep_sec != 0
- Chef::Log.trace("Sleeping for #{sleep_sec} seconds")
- sleep(sleep_sec)
- end
-
- run_chef_client
- unless Chef::Config[:interval]
- Chef::Application.exit! "Exiting", 0
- end
- rescue SystemExit => e
- raise
- rescue Exception => e
- if Chef::Config[:interval]
- Chef::Log.error("#{e.class}: #{e}")
- Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
- retry
- else
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
- end
- end
- end
-
- def fetch_recipe_tarball(url, path)
- Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
- File.open(path, "wb") do |f|
- open(url) do |r|
- f.write(r.read)
- end
- end
- end
-
- def unforked_interval_error_message
- "Unforked #{Chef::Dist::CLIENT} interval runs are disabled by default." +
- "\nConfiguration settings:" +
- ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
- "\nEnable #{Chef::Dist::CLIENT} interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- end
end