summaryrefslogtreecommitdiff
path: root/lib/chef/application
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2019-07-15 21:17:01 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2019-07-15 21:17:01 -0700
commit30ccdb8d5f56afe54d1a0ed38a4731e9a16adb4a (patch)
treef29a533481dacbe8b514f4add03546a6c934aee5 /lib/chef/application
parent1b8af54b67e14b9818fc6218ca874136a8191543 (diff)
downloadchef-30ccdb8d5f56afe54d1a0ed38a4731e9a16adb4a.tar.gz
Copy the daemon loop from client to base
These improvements never made it into solo and should be backwards compatible. Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
Diffstat (limited to 'lib/chef/application')
-rw-r--r--lib/chef/application/base.rb106
-rw-r--r--lib/chef/application/client.rb107
-rw-r--r--lib/chef/application/solo.rb50
3 files changed, 106 insertions, 157 deletions
diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb
index e83f786805..5f1d6255f1 100644
--- a/lib/chef/application/base.rb
+++ b/lib/chef/application/base.rb
@@ -195,12 +195,54 @@ class Chef::Application::Base < Chef::Application
target
}
+ 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
@@ -226,6 +268,70 @@ class Chef::Application::Base < Chef::Application
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:
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 876c16b27b..e13c6af049 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -138,9 +138,6 @@ class Chef::Application::Client < Chef::Application::Base
description: "Use cached cookbooks without overwriting local differences from the #{Chef::Dist::SERVER_PRODUCT}.",
boolean: false
- IMMEDIATE_RUN_SIGNAL = "1".freeze
- RECONFIGURE_SIGNAL = "H".freeze
-
# Reconfigure the chef client
# Re-open the JSON attributes and load them into the node
def reconfigure
@@ -238,108 +235,4 @@ class Chef::Application::Client < Chef::Application::Base
Ohai::Log.use_log_devices( Chef::Log )
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
end
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index 3c9abd05de..f8575ca79d 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -129,54 +129,4 @@ class Chef::Application::Solo < Chef::Application::Base
end
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 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
end