class Procodile::CLI
Attributes
config[RW]
options[RW]
Public Class Methods
command(name)
click to toggle source
# File lib/procodile/cli.rb, line 25 def self.command(name) commands[name] = {:name => name, :description => @description, :options => @options} @description = nil @options = nil end
commands()
click to toggle source
# File lib/procodile/cli.rb, line 13 def self.commands @commands ||= {} end
desc(description)
click to toggle source
# File lib/procodile/cli.rb, line 17 def self.desc(description) @description = description end
new()
click to toggle source
# File lib/procodile/cli.rb, line 34 def initialize @options = {} end
options(&block)
click to toggle source
# File lib/procodile/cli.rb, line 21 def self.options(&block) @options = block end
Private Class Methods
pid_active?(pid)
click to toggle source
# File lib/procodile/cli.rb, line 472 def self.pid_active?(pid) ::Process.getpgid(pid) ? true : false rescue Errno::ESRCH false end
start_supervisor(config, options = {}, &after_start)
click to toggle source
# File lib/procodile/cli.rb, line 496 def self.start_supervisor(config, options = {}, &after_start) run_options = {} run_options[:respawn] = options[:respawn] run_options[:stop_when_none] = options[:stop_when_none] run_options[:proxy] = options[:proxy] run_options[:force_single_log] = options[:foreground] run_options[:port_allocations] = options[:port_allocations] tidy_pids(config) if options[:clean] FileUtils.rm_rf(Dir[File.join(config.pid_root, '*')]) puts "Emptied PID directory" end if !Dir[File.join(config.pid_root, "*")].empty? raise Error, "The PID directory (#{config.pid_root}) is not empty. Cannot start unless things are clean." end $0="[procodile] #{config.app_name} (#{config.root})" if options[:foreground] File.open(config.supervisor_pid_path, 'w') { |f| f.write(::Process.pid) } Supervisor.new(config, run_options).start(&after_start) else FileUtils.rm_f(File.join(config.pid_root, "*.pid")) pid = fork do STDOUT.reopen(config.log_path, 'a') STDOUT.sync = true STDERR.reopen(config.log_path, 'a') STDERR.sync = true Supervisor.new(config, run_options).start(&after_start) end ::Process.detach(pid) File.open(config.supervisor_pid_path, 'w') { |f| f.write(pid) } puts "Started Procodile supervisor with PID #{pid}" end end
tidy_pids(config)
click to toggle source
# File lib/procodile/cli.rb, line 534 def self.tidy_pids(config) FileUtils.rm_f(config.supervisor_pid_path) FileUtils.rm_f(config.sock_path) pid_files = Dir[File.join(config.pid_root, '*.pid')] pid_files.each do |pid_path| file_name = pid_path.split('/').last pid = File.read(pid_path).to_i if self.pid_active?(pid) puts "Could not remove #{file_name} because process (#{pid}) was active" else FileUtils.rm_f(pid_path) puts "Removed #{file_name} because process was not active" end end end
Public Instance Methods
check_concurrency()
click to toggle source
# File lib/procodile/cli.rb, line 283 def check_concurrency if supervisor_running? reply = ControlClient.run(@config.sock_path, 'check_concurrency', :reload => @options[:reload]) if reply['started'].empty? && reply['stopped'].empty? puts "Processes are running as configured" else reply['started'].each do |instance| puts "Started".color(32) + " #{instance['description']} (PID: #{instance['pid']})" end reply['stopped'].each do |instance| puts "Stopped".color(31) + " #{instance['description']} (PID: #{instance['pid']})" end end else raise Error, "Procodile supervisor isn't running" end end
console()
click to toggle source
# File lib/procodile/cli.rb, line 405 def console if cmd = @config.console_command exec(cmd) else raise Error, "No console command has been configured in the Procfile" end end
dispatch(command)
click to toggle source
# File lib/procodile/cli.rb, line 38 def dispatch(command) if self.class.commands.keys.include?(command.to_sym) public_send(command) else raise Error, "Invalid command '#{command}'" end end
exec(command = nil)
click to toggle source
# File lib/procodile/cli.rb, line 369 def exec(command = nil) desired_command = command || ARGV.drop(1).join(' ') if prefix = @config.exec_prefix desired_command = "#{prefix} #{desired_command}" end if desired_command.length == 0 raise Error, "You need to specify a command to run (e.g. procodile run -- rake db:migrate)" else environment = @config.environment_variables unless ENV['PROCODILE_EXEC_QUIET'].to_i == 1 puts "Running with #{desired_command.color(33)}" for key, value in environment puts " #{key.color(34)} #{value}" end end begin Dir.chdir(@config.root) Rbenv.without do Kernel.exec(environment, desired_command) end rescue Errno::ENOENT => e raise Error, e.message end end end
Also aliased as: run
help()
click to toggle source
# File lib/procodile/cli.rb, line 51 def help puts "\e[45;37mWelcome to Procodile v#{Procodile::VERSION}\e[0m" puts "For documentation see https://adam.ac/procodile." puts puts "The following commands are supported:" puts self.class.commands.sort_by { |k,v| k.to_s }.each do |method, options| if options[:description] puts " \e[34m#{method.to_s.ljust(18, ' ')}\e[0m #{options[:description]}" end end puts puts "For details for the options available for each command, use the --help option." puts "For example 'procodile start --help'." end
kill()
click to toggle source
# File lib/procodile/cli.rb, line 352 def kill Dir[File.join(@config.pid_root, '*.pid')].each do |pid_path| name = pid_path.split('/').last.gsub(/\.pid\z/, '') pid = File.read(pid_path).to_i begin ::Process.kill('KILL', pid) puts "Sent KILL to #{pid} (#{name})" rescue Errno::ESRCH end FileUtils.rm(pid_path) end end
log()
click to toggle source
# File lib/procodile/cli.rb, line 431 def log opts = [] opts << "-f" if options[:wait] opts << "-n #{options[:lines]}" if options[:lines] if options[:process] if process = @config.processes[options[:process]] log_path = process.log_path else raise Error, "Invalid process name '#{options[:process]}'" end else log_path = @config.log_path end if File.exist?(log_path) exec("tail #{opts.join(' ')} #{log_path}") else raise Error, "No file found at #{log_path}" end end
reload()
click to toggle source
# File lib/procodile/cli.rb, line 264 def reload if supervisor_running? ControlClient.run(@config.sock_path, 'reload_config') puts "Reloaded Procodile config" else raise Error, "Procodile supervisor isn't running" end end
restart()
click to toggle source
# File lib/procodile/cli.rb, line 233 def restart if supervisor_running? instances = ControlClient.run(@config.sock_path, 'restart', :processes => process_names_from_cli_option, :tag => @options[:tag]) if instances.empty? puts "There are no processes to restart." else instances.each do |old_instance, new_instance| if old_instance && new_instance if old_instance['description'] == new_instance['description'] puts "Restarted".color(35) + " #{old_instance['description']}" else puts "Restarted".color(35) + " #{old_instance['description']} -> #{new_instance['description']}" end elsif old_instance puts "Stopped".color(31) + " #{old_instance['description']}" elsif new_instance puts "Started".color(32) + " #{new_instance['description']}" end $stdout.flush end end else raise Error, "Procodile supervisor isn't running" end end
start()
click to toggle source
# File lib/procodile/cli.rb, line 125 def start if supervisor_running? if @options[:foreground] raise Error, "Cannot be started in the foreground because supervisor already running" end if @options.has_key?(:respawn) raise Error, "Cannot disable respawning because supervisor is already running" end if @options[:stop_when_none] raise Error, "Cannot stop supervisor when none running because supervisor is already running" end if @options[:proxy] raise Error, "Cannot enable the proxy when the supervisor is running" end instances = ControlClient.run(@config.sock_path, 'start_processes', :processes => process_names_from_cli_option, :tag => @options[:tag], :port_allocations => @options[:port_allocations]) if instances.empty? puts "No processes to start." else instances.each do |instance| puts "Started".color(32) + " #{instance['description']} (PID: #{instance['pid']})" end end return else # The supervisor isn't actually running. We need to start it before processes can be # begin being processed if @options[:start_supervisor] == false raise Error, "Supervisor is not running and cannot be started because --no-supervisor is set" else self.class.start_supervisor(@config, @options) do |supervisor| unless @options[:start_processes] == false supervisor.start_processes(process_names_from_cli_option, :tag => @options[:tag]) end end end end end
status()
click to toggle source
# File lib/procodile/cli.rb, line 320 def status if supervisor_running? status = ControlClient.run(@config.sock_path, 'status') if @options[:json] puts status.to_json elsif @options[:json_pretty] puts JSON.pretty_generate(status) elsif @options[:simple] if status['messages'].empty? message = status['instances'].map { |p,i| "#{p}[#{i.size}]" } puts "OK || #{message.join(', ')}" else message = status['messages'].map { |p| Message.parse(p) }.join(', ') puts "Issues || #{message}" end else require 'procodile/status_cli_output' StatusCLIOutput.new(status).print_all end else if @options[:simple] puts "NotRunning || Procodile supervisor isn't running" else raise Error, "Procodile supervisor isn't running" end end end
stop()
click to toggle source
# File lib/procodile/cli.rb, line 186 def stop if supervisor_running? options = {} instances = ControlClient.run(@config.sock_path, 'stop', :processes => process_names_from_cli_option, :stop_supervisor => @options[:stop_supervisor]) if instances.empty? puts "No processes were stopped." else instances.each do |instance| puts "Stopped".color(31) + " #{instance['description']} (PID: #{instance['pid']})" end end if @options[:stop_supervisor] puts "Supervisor will be stopped when processes are stopped." end if @options[:wait_until_supervisor_stopped] puts "Waiting for supervisor to stop..." loop do sleep 1 if supervisor_running? sleep 1 else puts "Supervisor has stopped" exit 0 end end end else raise Error, "Procodile supervisor isn't running" end end
Private Instance Methods
current_pid()
click to toggle source
# File lib/procodile/cli.rb, line 463 def current_pid if File.exist?(@config.supervisor_pid_path) pid_file = File.read(@config.supervisor_pid_path).strip pid_file.length > 0 ? pid_file.to_i : nil else nil end end
process_names_from_cli_option()
click to toggle source
# File lib/procodile/cli.rb, line 478 def process_names_from_cli_option if @options[:processes] processes = @options[:processes].split(',') if processes.empty? raise Error, "No process names provided" end #processes.each do |process| # process_name, _ = process.split('.', 2) # unless @config.process_list.keys.include?(process_name.to_s) # raise Error, "Process '#{process_name}' is not configured. You may need to reload your config." # end #end processes else nil end end
supervisor_running?()
click to toggle source
# File lib/procodile/cli.rb, line 455 def supervisor_running? if pid = current_pid self.class.pid_active?(pid) else false end end