Class: Mizuno::Runner
- Inherits:
-
Object
- Object
- Mizuno::Runner
- Includes:
- Daemonizer
- Defined in:
- lib/mizuno/runner.rb
Overview
Launches Mizuno when called from the command-line, and handles damonization via FFI.
Daemonization code based on Spoon.
Class Method Summary collapse
- .connect_to_server_as_client(server_options, timeout) ⇒ Object
-
.daemonize(options) ⇒ Object
Relaunch as a daemon.
-
.die(message, success = false) ⇒ Object
Exit with a message and a status value.
-
.kill(options) ⇒ Object
Really stop a running daemon (SIGTERM).
-
.pid(options) ⇒ Object
Fetches the PID from the :pidfile.
-
.reload(options) ⇒ Object
Reload a running daemon by SIGHUPing it.
-
.resolve_path(root, path) ⇒ Object
Transform a relative path to an absolute path.
- .start(options) ⇒ Object
-
.start! ⇒ Object
Launch Jetty, optionally as a daemon.
-
.status(options) ⇒ Object
Return the status of a running daemon.
-
.stop(options) ⇒ Object
Stop a running daemon (SIGKILL).
-
.wait_for_server(options, timeout = 120) ⇒ Object
Wait until timeout seconds for a successful http connection; returns true if we could connect and didn’t get a server error, false otherwise.
-
.wait_for_server_to_die(options, timeout = 120) ⇒ Object
Like wait_for_server, but returns true when the server goes offline.
Methods included from Daemonizer
Class Method Details
.connect_to_server_as_client(server_options, timeout) ⇒ Object
270 271 272 273 274 275 276 277 |
# File 'lib/mizuno/runner.rb', line 270 def Runner.connect_to_server_as_client(, timeout) = .dup [:host] = '127.0.0.1' if [:host] == "0.0.0.0" Net::HTTP.start([:host], [:port]) do |http| http.read_timeout = timeout http.get("/") end end |
.daemonize(options) ⇒ Object
Relaunch as a daemon.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/mizuno/runner.rb', line 120 def Runner.daemonize() # Ensure that Mizuno isn't running. Runner.pid() and die("Mizuno is already running.") # Build a command line that should launch JRuby with the # appropriate options; this depends on the proper jruby # being in the $PATH config = .delete(:config) args = Mizuno::LAUNCH_ENV.concat(.map { |k, v| (v.to_s.empty?) ? nil : [ "--#{k}", v.to_s ] }.compact.flatten) args.push(config) args.unshift('jruby') # Launch a detached child process. child = ChildProcess.build(*args) child.io.inherit! child.detach = true child.start File.open([:pidfile], 'w') { |f| f.puts(child.pid) } # Wait until the server starts or we time out waiting for it. exit if wait_for_server(, 60) child.stop die("Failed to start Mizuno.") end |
.die(message, success = false) ⇒ Object
Exit with a message and a status value.
FIXME: Dump these in the logfile if called from Server?
284 285 286 287 |
# File 'lib/mizuno/runner.rb', line 284 def Runner.die(, success = false) $stderr.puts() exit(success ? 0 : 1) end |
.kill(options) ⇒ Object
Really stop a running daemon (SIGTERM)
184 185 186 187 188 189 190 191 |
# File 'lib/mizuno/runner.rb', line 184 def Runner.kill() pid = Runner.pid() or die("Mizuno isn't running.") $stderr.puts "Terminating Mizuno with extreme prejudice..." Process.kill("TERM", pid) die("failed") unless wait_for_server_to_die() FileUtils.rm([:pidfile]) die("stopped", true) end |
.pid(options) ⇒ Object
Fetches the PID from the :pidfile.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/mizuno/runner.rb', line 205 def Runner.pid() [:pidfile] or die("Speficy a --pidfile to daemonize.") return unless File.exists?([:pidfile]) pid = File.read([:pidfile]).to_i # FIXME: This is a hacky way to get the process list, but I # haven't found a good cross-platform solution yet; this # should work on MacOS and Linux, possibly Solaris and BSD, # and almost definitely not on Windows. process = `ps ax`.lines.select { |l| l =~ /^\s*#{pid}\s*/ } return(pid) if (process.join =~ /\bmizuno\b/) # Stale pidfile; remove. $stderr.puts("Removing stale pidfile '#{options[:pidfile]}'") FileUtils.rm([:pidfile]) return(nil) end |
.reload(options) ⇒ Object
Reload a running daemon by SIGHUPing it.
160 161 162 163 164 165 166 167 |
# File 'lib/mizuno/runner.rb', line 160 def Runner.reload() pid = Runner.pid() return(Runner.daemonize()) \ if(pid.nil? and .delete(:restart)) die("Mizuno is currently not running.") unless pid Process.kill("HUP", pid) die("Mizuno signaled to reload app.", true) end |
.resolve_path(root, path) ⇒ Object
Transform a relative path to an absolute path.
196 197 198 199 200 |
# File 'lib/mizuno/runner.rb', line 196 def Runner.resolve_path(root, path) return(path) unless path.is_a?(String) return(path) if (path =~ /^\//) File.(File.join(root, path)) end |
.start(options) ⇒ Object
107 108 109 110 111 112 113 114 115 |
# File 'lib/mizuno/runner.rb', line 107 def Runner.start() Dir.chdir([:root]) Logger.configure() ENV['RACK_ENV'] = [:env] server = Rack::Server.new server. = .merge(:server => 'mizuno', :environment => [:env]) server.start end |
.start! ⇒ Object
Launch Jetty, optionally as a daemon.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/mizuno/runner.rb', line 79 def Runner.start! # Default rackup is in config.ru config = (Choice.rest.first or "config.ru") # Create an options hash with only symbols. choices = Choice.choices.merge(:config => config) = Hash[choices.map { |k, v| [ k.to_sym, v ] }] # Resolve relative paths to the logfile, etc. root = [:root] [:pidfile] = Runner.resolve_path(root, [:pidfile]) [:log] = Runner.resolve_path(root, [:log]) [:public] = Runner.resolve_path(root, [:public]) # Require multiple libraries. .delete(:require).each { |r| require r } # Handle daemon-related commands. Runner.status() if .delete(:status) Runner.reload() if .delete(:reload) Runner.stop() if .delete(:stop) Runner.kill() if .delete(:kill) Runner.daemonize() if .delete(:daemonize) # Fire up Mizuno as if it was called from Rackup. Runner.start() end |
.status(options) ⇒ Object
Return the status of a running daemon.
149 150 151 152 153 154 155 |
# File 'lib/mizuno/runner.rb', line 149 def Runner.status() die("Mizuno doesn't appear to be running.") \ unless (pid = Runner.pid()) die("Mizuno is running, but not online.") \ unless(wait_for_server()) die("Mizuno is running.", true) end |
.stop(options) ⇒ Object
Stop a running daemon (SIGKILL)
172 173 174 175 176 177 178 179 |
# File 'lib/mizuno/runner.rb', line 172 def Runner.stop() pid = Runner.pid() or die("Mizuno isn't running.") print "Stopping Mizuno..." Process.kill("KILL", pid) die("failed") unless wait_for_server_to_die() FileUtils.rm([:pidfile]) die("stopped", true) end |
.wait_for_server(options, timeout = 120) ⇒ Object
Wait until timeout seconds for a successful http connection; returns true if we could connect and didn’t get a server error, false otherwise.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/mizuno/runner.rb', line 228 def Runner.wait_for_server(, timeout = 120) force_time_out_at = Time.now + timeout sleep_interval_for_next_retry = 0.1 begin response = connect_to_server_as_client(, timeout) return(response.code.to_i < 500) rescue Errno::ECONNREFUSED => error return(false) if (Time.now > force_time_out_at) sleep(sleep_interval_for_next_retry) sleep_interval_for_next_retry *= 2 retry rescue => error puts "HTTP Error '#{error}'" return(false) end end |
.wait_for_server_to_die(options, timeout = 120) ⇒ Object
Like wait_for_server, but returns true when the server goes offline. If we hit timeout seconds and the server is still responding, returns false.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/mizuno/runner.rb', line 251 def Runner.wait_for_server_to_die(, timeout = 120) force_time_out_at = Time.now + timeout sleep_interval_for_next_retry = 0.1 begin while (Time.now < force_time_out_at) connect_to_server_as_client(, timeout) sleep(sleep_interval_for_next_retry) sleep_interval_for_next_retry *= 2 end return(false) rescue Errno::ECONNREFUSED => error return(true) rescue => error puts "**** http error: #{error}" return(true) end end |