Class: Sunshine::Daemon
- Inherits:
-
Object
- Object
- Sunshine::Daemon
- Defined in:
- lib/sunshine/daemon.rb
Overview
An abstract class to wrap simple daemon software setup and start/stop.
Child classes are expected to at least provide a start and stop bash script by either overloading the start_cmd and stop_cmd methods, or by setting may also be specified if restart requires more functionality than simply calling start_cmd && stop_cmd.
Direct Known Subclasses
Constant Summary collapse
- START_FAILED_CODE =
10
- STOP_FAILED_CODE =
11
- RESTART_FAILED_CODE =
12
- STATUS_DOWN_CODE =
13
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
Returns the value of attribute app.
-
#bin ⇒ Object
Returns the value of attribute bin.
-
#config_file ⇒ Object
Returns the value of attribute config_file.
-
#config_path ⇒ Object
Returns the value of attribute config_path.
-
#config_template ⇒ Object
Returns the value of attribute config_template.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#pid ⇒ Object
Returns the value of attribute pid.
-
#processes ⇒ Object
Returns the value of attribute processes.
-
#restart_cmd ⇒ Object
Gets the command that restarts the daemon.
-
#server_apps ⇒ Object
Returns the value of attribute server_apps.
-
#start_cmd ⇒ Object
Gets the command that starts the daemon.
-
#status_cmd ⇒ Object
Status command wrapped with an exit_on_failure handler.
-
#stop_cmd ⇒ Object
Default daemon stop command.
-
#sudo ⇒ Object
Returns the value of attribute sudo.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
Class Method Summary collapse
-
.binder_methods ⇒ Object
Returns an array of method names to assign to the binder for template rendering.
-
.short_name ⇒ Object
Returns the short, snake-case version of the class: Sunshine::Daemon.short_name #=> “daemon”.
-
.underscore(str) ⇒ Object
Turn camelcase into underscore.
Instance Method Summary collapse
-
#_restart_cmd ⇒ Object
Restart command wrapped with an exit_on_failure handler.
-
#_start_cmd ⇒ Object
Start command wrapped with an exit_on_failure handler.
-
#_status_cmd ⇒ Object
Get the command to check if the daemon is running.
-
#_stop_cmd ⇒ Object
Stop command wrapped with an exit_on_failure handler.
-
#chown_log_files(shell) ⇒ Object
Make sure log files are owned by the daemon’s user.
-
#config_binding(shell) ⇒ Object
Create and setup a binding for a given shell.
-
#config_file_path ⇒ Object
Get the file path to the daemon’s config file.
-
#config_template_files ⇒ Object
Get the array of local config template files needed by the daemon.
-
#configure_remote_dirs(shell) ⇒ Object
Make sure all the remote directories needed by the daemon exist.
-
#each_server_app(&block) ⇒ Object
Do something with each server app used by the daemon.
-
#exit_on_failure(cmd, exitcode = 1, message = nil) ⇒ Object
Wrap a command with a fail-specific exitcode and message.
-
#has_setup?(force = false) ⇒ Boolean
Check if setup was run successfully.
-
#initialize(app, options = {}) ⇒ Daemon
constructor
Daemon objects need only an App object to be instantiated but many options are available for customization:.
-
#log_file(key) ⇒ Object
Get the path of a log file: daemon.log_file(:stderr) #=> “/all_logs/stderr.log”.
-
#log_files(hash) ⇒ Object
Append or override daemon log file paths: daemon.log_files :stderr => “/all_logs/stderr.log”.
-
#pick_sudo(shell) ⇒ Object
Pick which sudo to use between the daemon sudo and shell sudo.
-
#register_after_user_script ⇒ Object
Setup what should be run after the user block on App#deploy.
-
#restart ⇒ Object
Restarts the daemon using the restart_cmd attribute if provided.
-
#setup ⇒ Object
Setup the daemon, parse and upload config templates.
-
#start ⇒ Object
Start the daemon app after running setup.
-
#status ⇒ Object
Check if the daemon is running on all servers.
-
#stop ⇒ Object
Stop the daemon app.
-
#upload_config_files(shell, setup_binding = binding) ⇒ Object
Upload config files and run them through erb with the provided binding if necessary.
Constructor Details
#initialize(app, options = {}) ⇒ Daemon
Daemon objects need only an App object to be instantiated but many options are available for customization:
- :bin
-
bin_path - Set the daemon app bin path (e.g. usr/local/nginx)
defaults to svr_name.
- :processes
-
prcss_num - Number of processes daemon should run;
defaults to 1.
- :config_file
-
name - Remote file name the daemon should load;
defaults to svr_name.conf
- :config_path
-
path - Remote path daemon configs will be uploaded to;
defaults to app.current_path/daemons/svr_name
- :config_template
-
path - Glob path to tempates to render and upload;
defaults to sunshine_path/templates/svr_name/*
- :log_path
-
path - Path to where the log files should be output;
defaults to app.log_path.
- :pid
-
pid_path - Set the pid; default: app.shared_path/pids/svr_name.pid
defaults to app.shared_path/pids/svr_name.pid.
- :sudo
-
bool|str - Define if sudo should be used to run the daemon,
and/or with what user.
- :timeout
-
int - Timeout to use for daemon config, defaults to 0.
The Daemon constructor also supports any App#find options to narrow the server apps to use. Note: subclasses such as Server already have a default :role that can be overridden.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/sunshine/daemon.rb', line 89 def initialize app, ={} @options = @app = app @name = [:name] || self.class.short_name @pid = [:pid] || "#{@app.shared_path}/pids/#{@name}.pid" @bin = [:bin] || self.class.short_name @sudo = [:sudo] @timeout = [:timeout] || 0 @dep_name = [:dep_name] || self.class.short_name @processes = [:processes] || 1 @sigkill = 'QUIT' @config_template = [:config_template] || "#{Sunshine::ROOT}/templates/#{self.class.short_name}/*" @config_path = [:config_path] || "#{@app.current_path}/daemons/#{@name}" @config_file = [:config_file] || "#{self.class.short_name}.conf" log_path = [:log_path] || @app.log_path @log_files = { :stderr => "#{log_path}/#{@name}_stderr.log", :stdout => "#{log_path}/#{@name}_stdout.log" } @start_cmd = @stop_cmd = @restart_cmd = @status_cmd = nil @setup_successful = nil register_after_user_script end |
Instance Attribute Details
#app ⇒ Object (readonly)
Returns the value of attribute app.
47 48 49 |
# File 'lib/sunshine/daemon.rb', line 47 def app @app end |
#bin ⇒ Object
Returns the value of attribute bin.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def bin @bin end |
#config_file ⇒ Object
Returns the value of attribute config_file.
51 52 53 |
# File 'lib/sunshine/daemon.rb', line 51 def config_file @config_file end |
#config_path ⇒ Object
Returns the value of attribute config_path.
51 52 53 |
# File 'lib/sunshine/daemon.rb', line 51 def config_path @config_path end |
#config_template ⇒ Object
Returns the value of attribute config_template.
51 52 53 |
# File 'lib/sunshine/daemon.rb', line 51 def config_template @config_template end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
47 48 49 |
# File 'lib/sunshine/daemon.rb', line 47 def name @name end |
#pid ⇒ Object
Returns the value of attribute pid.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def pid @pid end |
#processes ⇒ Object
Returns the value of attribute processes.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def processes @processes end |
#restart_cmd ⇒ Object
Gets the command that restarts the daemon. Should be overridden by child classes if different from start_cmd && stop_cmd.
311 312 313 |
# File 'lib/sunshine/daemon.rb', line 311 def restart_cmd @restart_cmd end |
#server_apps ⇒ Object
Returns the value of attribute server_apps.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def server_apps @server_apps end |
#start_cmd ⇒ Object
Gets the command that starts the daemon. Should be overridden by child classes.
273 274 275 276 |
# File 'lib/sunshine/daemon.rb', line 273 def start_cmd return @start_cmd if @start_cmd raise DaemonError, "start_cmd undefined for #{@name}" end |
#status_cmd ⇒ Object
Status command wrapped with an exit_on_failure handler.
332 333 334 |
# File 'lib/sunshine/daemon.rb', line 332 def status_cmd @status_cmd || "test -f #{@pid} && kill -0 $(cat #{@pid})" end |
#stop_cmd ⇒ Object
Default daemon stop command.
291 292 293 294 |
# File 'lib/sunshine/daemon.rb', line 291 def stop_cmd "test -f #{@pid} && kill -#{@sigkill} $(cat #{@pid}) && sleep 1 && "+ "rm -f #{@pid}" end |
#sudo ⇒ Object
Returns the value of attribute sudo.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def sudo @sudo end |
#timeout ⇒ Object
Returns the value of attribute timeout.
49 50 51 |
# File 'lib/sunshine/daemon.rb', line 49 def timeout @timeout end |
Class Method Details
.binder_methods ⇒ Object
Returns an array of method names to assign to the binder for template rendering.
23 24 25 |
# File 'lib/sunshine/daemon.rb', line 23 def self.binder_methods [:app, :name, :bin, :pid, :processes, :config_path, :log_file, :timeout] end |
.short_name ⇒ Object
33 34 35 |
# File 'lib/sunshine/daemon.rb', line 33 def self.short_name @short_name ||= self.underscore self.to_s.split("::").last end |
.underscore(str) ⇒ Object
Turn camelcase into underscore. Used for Daemon#name.
41 42 43 44 |
# File 'lib/sunshine/daemon.rb', line 41 def self.underscore str str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase end |
Instance Method Details
#_restart_cmd ⇒ Object
Restart command wrapped with an exit_on_failure handler.
319 320 321 322 323 324 325 326 |
# File 'lib/sunshine/daemon.rb', line 319 def _restart_cmd if restart_cmd exit_on_failure restart_cmd, RESTART_FAILED_CODE, "Could not restart #{@name} for #{@app.name}" else "(#{_stop_cmd}) && (#{_start_cmd});" end end |
#_start_cmd ⇒ Object
Start command wrapped with an exit_on_failure handler.
282 283 284 285 |
# File 'lib/sunshine/daemon.rb', line 282 def _start_cmd exit_on_failure start_cmd, START_FAILED_CODE, "Could not start #{@name} for #{@app.name}" end |
#_status_cmd ⇒ Object
Get the command to check if the daemon is running.
340 341 342 343 |
# File 'lib/sunshine/daemon.rb', line 340 def _status_cmd exit_on_failure status_cmd, STATUS_DOWN_CODE, "#{@app.name} #{@name} is not running" end |
#_stop_cmd ⇒ Object
Stop command wrapped with an exit_on_failure handler.
300 301 302 303 |
# File 'lib/sunshine/daemon.rb', line 300 def _stop_cmd exit_on_failure stop_cmd, STOP_FAILED_CODE, "Could not kill #{@name} pid for #{@app.name}" end |
#chown_log_files(shell) ⇒ Object
Make sure log files are owned by the daemon’s user.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/sunshine/daemon.rb', line 447 def chown_log_files shell files = @log_files.values.join(" ") sudo = pick_sudo(shell) user = case sudo when true then 'root' when String then sudo else nil end return unless user shell.call "chown -f #{user} #{files}", :sudo => true rescue nil end |
#config_binding(shell) ⇒ Object
Create and setup a binding for a given shell.
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/sunshine/daemon.rb', line 403 def config_binding shell binder = Binder.new self binder.forward(*self.class.binder_methods) binder.set :shell, shell binder_sudo = pick_sudo(shell) binder.set :sudo, binder_sudo binder.set :expand_path do |path| shell. path end binder end |
#config_file_path ⇒ Object
Get the file path to the daemon’s config file.
368 369 370 |
# File 'lib/sunshine/daemon.rb', line 368 def config_file_path "#{@config_path}/#{@config_file}" end |
#config_template_files ⇒ Object
Get the array of local config template files needed by the daemon.
395 396 397 |
# File 'lib/sunshine/daemon.rb', line 395 def config_template_files @config_template_files ||= Dir[@config_template].select{|f| File.file?(f)} end |
#configure_remote_dirs(shell) ⇒ Object
Make sure all the remote directories needed by the daemon exist.
432 433 434 435 436 437 438 439 440 441 |
# File 'lib/sunshine/daemon.rb', line 432 def configure_remote_dirs shell dirs = @log_files.values.map{|f| File.dirname(f)} dirs << File.dirname(@pid) dirs << @config_path dirs.delete_if{|d| d == "."} dirs = dirs.join(" ") shell.call "mkdir -p #{dirs}" end |
#each_server_app(&block) ⇒ Object
Do something with each server app used by the daemon.
127 128 129 |
# File 'lib/sunshine/daemon.rb', line 127 def each_server_app(&block) @app.each(@options, &block) end |
#exit_on_failure(cmd, exitcode = 1, message = nil) ⇒ Object
Wrap a command with a fail-specific exitcode and message.
264 265 266 |
# File 'lib/sunshine/daemon.rb', line 264 def exit_on_failure cmd, exitcode=1, =nil "(#{cmd}) || (echo '#{}' && exit #{exitcode});" end |
#has_setup?(force = false) ⇒ Boolean
Check if setup was run successfully.
170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/sunshine/daemon.rb', line 170 def has_setup? force=false return @setup_successful unless @setup_successful.nil? || force each_server_app do |server_app| unless server_app.shell.file? config_file_path return @setup_successful = false end end @setup_successful = true end |
#log_file(key) ⇒ Object
Get the path of a log file:
daemon.log_file(:stderr)
#=> "/all_logs/stderr.log"
360 361 362 |
# File 'lib/sunshine/daemon.rb', line 360 def log_file key @log_files[key] end |
#log_files(hash) ⇒ Object
Append or override daemon log file paths:
daemon.log_files :stderr => "/all_logs/stderr.log"
350 351 352 |
# File 'lib/sunshine/daemon.rb', line 350 def log_files hash @log_files.merge!(hash) end |
#pick_sudo(shell) ⇒ Object
Pick which sudo to use between the daemon sudo and shell sudo. (Useful when running servers on ports < 1024)
424 425 426 |
# File 'lib/sunshine/daemon.rb', line 424 def pick_sudo shell self.sudo.nil? ? shell.sudo : self.sudo end |
#register_after_user_script ⇒ Object
Setup what should be run after the user block on App#deploy.
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/sunshine/daemon.rb', line 466 def register_after_user_script @app.after_user_script do |app| next unless has_setup? each_server_app do |sa| sudo = pick_sudo sa.shell %w{start stop restart status}.each do |script| script_file = "#{@config_path}/#{script}" cmd = send "_#{script}_cmd" sa.shell.make_file script_file, cmd, :flags => '--chmod=ugo=rwx' cmd = sa.shell.sudo_cmd "#{@app.root_path}/env #{script_file}", sudo sa.scripts[script.to_sym] << [*cmd].join(" ") end end end end |
#restart ⇒ Object
Restarts the daemon using the restart_cmd attribute if provided. If restart_cmd is not provided, calls stop and start.
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/sunshine/daemon.rb', line 243 def restart self.setup unless has_setup? Sunshine.logger.info @name, "Restarting #{@name} daemon" do each_server_app do |server_app| begin server_app.shell.call _restart_cmd, :sudo => pick_sudo(server_app.shell) yield(server_app) if block_given? rescue => e raise DaemonError.new(e, "Could not restart #{@name}") end end end end |
#setup ⇒ Object
Setup the daemon, parse and upload config templates. If a dependency with the daemon name exists in Sunshine.dependencies, setup will attempt to install the dependency before uploading configs. If a block is given it will be passed each server_app and binder object which will be used for the building erb config templates. See the ConfigBinding class for more information.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/sunshine/daemon.rb', line 140 def setup Sunshine.logger.info @name, "Setting up #{@name} daemon" do each_server_app do |server_app| # Build erb binding binder = config_binding server_app.shell configure_remote_dirs server_app.shell chown_log_files server_app.shell yield(server_app, binder) if block_given? server_app.install_deps @dep_name if Sunshine.dependencies.exist?(@dep_name) upload_config_files(server_app.shell, binder.get_binding) end end @setup_successful = true rescue => e raise DaemonError.new(e, "Could not setup #{@name}") end |
#start ⇒ Object
Start the daemon app after running setup.
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/sunshine/daemon.rb', line 187 def start self.setup unless has_setup? Sunshine.logger.info @name, "Starting #{@name} daemon" do each_server_app do |server_app| begin server_app.shell.call _start_cmd, :sudo => pick_sudo(server_app.shell) yield(server_app) if block_given? rescue => e raise DaemonError.new(e, "Could not start #{@name}") end end end end |
#status ⇒ Object
Check if the daemon is running on all servers
208 209 210 211 212 213 214 215 216 |
# File 'lib/sunshine/daemon.rb', line 208 def status each_server_app do |server_app| server_app.shell.call _status_cmd, :sudo => pick_sudo(server_app.shell) end true rescue CmdError => e false end |
#stop ⇒ Object
Stop the daemon app.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/sunshine/daemon.rb', line 222 def stop Sunshine.logger.info @name, "Stopping #{@name} daemon" do each_server_app do |server_app| begin server_app.shell.call _stop_cmd, :sudo => pick_sudo(server_app.shell) yield(server_app) if block_given? rescue => e raise DaemonError.new(e, "Could not stop #{@name}") end end end end |
#upload_config_files(shell, setup_binding = binding) ⇒ Object
Upload config files and run them through erb with the provided binding if necessary.
377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/sunshine/daemon.rb', line 377 def upload_config_files shell, setup_binding=binding config_template_files.each do |config_file| if File.extname(config_file) == ".erb" filename = File.basename(config_file[0..-5]) parsed_config = @app.build_erb(config_file, setup_binding) shell.make_file "#{@config_path}/#{filename}", parsed_config else filename = File.basename(config_file) shell.upload config_file, "#{@config_path}/#{filename}" end end end |