Class: Subserver::CLI
- Inherits:
-
Object
- Object
- Subserver::CLI
- Includes:
- Singleton, Util
- Defined in:
- lib/subserver/cli.rb
Constant Summary collapse
- SIGNAL_HANDLERS =
{ # Ctrl-C in terminal 'INT' => ->(cli) { raise Interrupt }, # TERM is the signal that Subserver must exit. # Heroku sends TERM and then waits 30 seconds for process to exit. 'TERM' => ->(cli) { raise Interrupt }, 'USR1' => ->(cli) { Subserver.logger.info "Received USR1, no longer accepting new work" cli.launcher.quiet }, 'TSTP' => ->(cli) { Subserver.logger.info "Received TSTP, no longer accepting new work" cli.launcher.quiet }, 'USR2' => ->(cli) { if Subserver.[:logfile] Subserver.logger.info "Received USR2, reopening log file" Subserver::Logging.reopen_logs end }, 'TTIN' => ->(cli) { Thread.list.each do |thread| Subserver.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread['subserver_label']}" if thread.backtrace Subserver.logger.warn thread.backtrace.join("\n") else Subserver.logger.warn "<no backtrace available>" end end }, }
Constants included from Util
Instance Attribute Summary collapse
-
#code ⇒ Object
Returns the value of attribute code.
-
#environment ⇒ Object
Returns the value of attribute environment.
-
#launcher ⇒ Object
Returns the value of attribute launcher.
Class Method Summary collapse
Instance Method Summary collapse
- #boot_system ⇒ Object
- #daemonize ⇒ Object
- #default_tag ⇒ Object
- #handle_signal(sig) ⇒ Object
-
#initialize ⇒ CLI
constructor
A new instance of CLI.
- #initialize_logger ⇒ Object
- #jruby? ⇒ Boolean
- #options ⇒ Object
- #parse(args = ARGV) ⇒ Object
- #parse_options(argv) ⇒ Object
- #print_banner ⇒ Object
- #run ⇒ Object
- #set_environment(cli_env) ⇒ Object
- #setup_options(args) ⇒ Object
- #validate! ⇒ Object
- #write_pid ⇒ Object
Methods included from Util
#fire_event, #hostname, #identity, #logger, #process_nonce, #safe_thread, #watchdog
Methods included from ExceptionHandler
Constructor Details
#initialize ⇒ CLI
Returns a new instance of CLI.
22 23 24 |
# File 'lib/subserver/cli.rb', line 22 def initialize @code = nil end |
Instance Attribute Details
#code ⇒ Object
Returns the value of attribute code.
18 19 20 |
# File 'lib/subserver/cli.rb', line 18 def code @code end |
#environment ⇒ Object
Returns the value of attribute environment.
20 21 22 |
# File 'lib/subserver/cli.rb', line 20 def environment @environment end |
#launcher ⇒ Object
Returns the value of attribute launcher.
19 20 21 |
# File 'lib/subserver/cli.rb', line 19 def launcher @launcher end |
Class Method Details
.banner ⇒ Object
123 124 125 126 127 128 129 |
# File 'lib/subserver/cli.rb', line 123 def self. %q{ ================================ Subserver ================================ } end |
Instance Method Details
#boot_system ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/subserver/cli.rb', line 233 def boot_system ENV['RACK_ENV'] = ENV['RAILS_ENV'] = environment raise ArgumentError, "#{[:require]} does not exist" unless File.exist?([:require]) if File.directory?([:require]) require 'rails' if ::Rails::VERSION::MAJOR < 4 raise "Subserver does not support this version of Rails." elsif ::Rails::VERSION::MAJOR == 4 require File.("#{[:require]}/config/application.rb") ::Rails::Application.initializer "subserver.eager_load" do ::Rails.application.config.eager_load = true end require 'subserver/rails' require File.("#{[:require]}/config/environment.rb") else require 'subserver/rails' require File.("#{[:require]}/config/environment.rb") end [:tag] ||= default_tag else = "#{[:require]} was not required, you should use an explicit path: " + "./#{[:require]} or /path/to/#{[:require]}" require([:require]) || raise(ArgumentError, ) end end |
#daemonize ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/subserver/cli.rb', line 182 def daemonize return unless [:daemon] raise ArgumentError, "You really should set a logfile if you're going to daemonize" unless [:logfile] files_to_reopen = [] ObjectSpace.each_object(File) do |file| files_to_reopen << file unless file.closed? end ::Process.daemon(true, true) files_to_reopen.each do |file| begin file.reopen file.path, "a+" file.sync = true rescue ::Exception end end [$stdout, $stderr].each do |io| File.open([:logfile], 'ab') do |f| io.reopen(f) end io.sync = true end $stdin.reopen('/dev/null') initialize_logger end |
#default_tag ⇒ Object
262 263 264 265 266 267 268 269 270 271 |
# File 'lib/subserver/cli.rb', line 262 def default_tag dir = ::Rails.root name = File.basename(dir) if name.to_i != 0 && prevdir = File.dirname(dir) # Capistrano release directory? if File.basename(prevdir) == 'releases' return File.basename(File.dirname(prevdir)) end end name end |
#handle_signal(sig) ⇒ Object
163 164 165 166 167 168 169 170 171 |
# File 'lib/subserver/cli.rb', line 163 def handle_signal(sig) Subserver.logger.debug "Got #{sig} signal" handy = SIGNAL_HANDLERS[sig] if handy handy.call(self) else Subserver.logger.info { "No signal handler for #{sig}" } end end |
#initialize_logger ⇒ Object
358 359 360 361 |
# File 'lib/subserver/cli.rb', line 358 def initialize_logger Subserver::Logging.initialize_logger([:logfile]) if [:logfile] Subserver.logger.level = ::Logger::DEBUG if [:verbose] end |
#jruby? ⇒ Boolean
36 37 38 |
# File 'lib/subserver/cli.rb', line 36 def jruby? defined?(::JRUBY_VERSION) end |
#options ⇒ Object
229 230 231 |
# File 'lib/subserver/cli.rb', line 229 def Subserver. end |
#parse(args = ARGV) ⇒ Object
26 27 28 29 30 31 32 33 34 |
# File 'lib/subserver/cli.rb', line 26 def parse(args=ARGV) @code = nil (args) initialize_logger validate! daemonize write_pid end |
#parse_options(argv) ⇒ Object
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/subserver/cli.rb', line 289 def (argv) opts = {} @parser = OptionParser.new do |o| o.on "-c", "--credentials PATH", "Path to Google Cloud credentials JSON file." do |arg| opts[:credentials] = arg end o.on '-d', '--daemon', "Daemonize process" do |arg| opts[:daemon] = arg end o.on '-e', '--environment ENV', "Application environment" do |arg| opts[:environment] = arg end o.on '-g', '--tag TAG', "Process tag for procline" do |arg| opts[:tag] = arg end o.on '-p', '--project ID', "Google Cloud Project ID" do |arg| opts[:project_id] = arg end o.on "-q", "--queue QUEUE", "Subscriber queues to process with this server" do |arg| queue = arg opts = (opts[:queues] ||= []) << queue end o.on '-r', '--require [PATH|DIR]', "Location of Rails application with subscribers or file to require" do |arg| opts[:require] = arg end o.on '-t', '--timeout NUM', "Shutdown timeout" do |arg| opts[:timeout] = Integer(arg) end o.on "-v", "--verbose", "Print more verbose output" do |arg| opts[:verbose] = arg end o.on '-C', '--config PATH', "path to YAML config file" do |arg| opts[:config_file] = arg end o.on '-L', '--logfile PATH', "path to writable logfile" do |arg| opts[:logfile] = arg end o.on '-P', '--port PORT', "port to expose health check on" do |arg| opts[:health_port] = arg end o.on '-V', '--version', "Print version and exit" do |arg| puts "Subserver #{Subserver::VERSION}" die(0) end end @parser. = "subserver [options]" @parser.on_tail "-h", "--help", "Show help" do logger.info @parser die 1 end @parser.parse!(argv) opts end |
#print_banner ⇒ Object
175 176 177 178 179 180 |
# File 'lib/subserver/cli.rb', line 175 def # Print logo and banner for development if environment == 'development' && $stdout.tty? puts Subserver::CLI. end end |
#run ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/subserver/cli.rb', line 40 def run boot_system self_read, self_write = IO.pipe sigs = %w(INT TERM TTIN TSTP) # USR1 and USR2 don't work on the JVM if !jruby? sigs << 'USR1' sigs << 'USR2' end sigs.each do |sig| begin trap sig do self_write.write("#{sig}\n") end rescue ArgumentError puts "Signal #{sig} not supported" end end logger.info "Running in #{RUBY_DESCRIPTION}" logger.info Subserver::LICENSE # cache process identity Subserver.[:identity] = identity # Touch middleware so it isn't lazy loaded by multiple threads. Subserver.middleware # Test Pubsub Connection if ENV['PUBSUB_EMULATOR_HOST'] uri = URI.parse("http://#{ENV['PUBSUB_EMULATOR_HOST']}") http = Net::HTTP.new(uri.host, uri.port) begin response = http.request_get(uri) rescue Errno::ECONNREFUSED logger.error "Errno::ECONNREFUSED - Could not connect to Pubsub Emulator at connection: #{ENV['PUBSUB_EMULATOR_HOST']}." logger.info "If you are not intending to connect to the Pubsub Emulator remove the PUBSUB_EMULATOR_HOST environment variable." die(1) end else begin client = Subserver::Pubsub.client rescue StandardError => e logger.error "Pubsub Connection Error: #{e.}" die(1) end end # Until this point, the process is initializing with just the main thread. # After this point the process will have multiple threads running. fire_event(:startup, reverse: false, reraise: true) logger.debug { "Middleware: #{Subserver.middleware.map(&:klass).join(', ')}" } if ![:daemon] logger.info 'Starting processing, hit Ctrl-C to stop' end # Start Health Server @health_thread = safe_thread("health_server") do Subserver.health_server.start end require 'subserver/launcher' @launcher = Subserver::Launcher.new() begin launcher.run while readable_io = IO.select([self_read]) signal = readable_io.first[0].gets.strip handle_signal(signal) end rescue Interrupt logger.info 'Shutting down' launcher.stop exit(0) end end |
#set_environment(cli_env) ⇒ Object
212 213 214 |
# File 'lib/subserver/cli.rb', line 212 def set_environment(cli_env) @environment = cli_env || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development' end |
#setup_options(args) ⇒ Object
219 220 221 222 223 224 225 226 227 |
# File 'lib/subserver/cli.rb', line 219 def (args) opts = (args) set_environment opts[:environment] cfile = opts[:config_file] opts = Subserver.load_config(cfile).merge(opts) Subserver. = opts end |
#validate! ⇒ Object
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/subserver/cli.rb', line 273 def validate! [:queues] << 'default' if [:queues].empty? if !File.exist?([:require]) || (File.directory?([:require]) && !File.exist?("#{[:require]}/config/application.rb")) logger.info "==================================================================" logger.info " Please point subserver to a Rails 4/5 application or a Ruby file " logger.info " to load your subscriber classes with -r [DIR|FILE]." logger.info "==================================================================" logger.info @parser die(1) end raise ArgumentError, "#{timeout}: #{[:timeout]} is not a valid value" if .has_key?(:timeout) && [:timeout].to_i <= 0 end |
#write_pid ⇒ Object
363 364 365 366 367 368 369 370 |
# File 'lib/subserver/cli.rb', line 363 def write_pid if path = [:pidfile] pidfile = File.(path) File.open(pidfile, 'w') do |f| f.puts ::Process.pid end end end |