initial commit
This commit is contained in:
7
bin/foreman
Executable file
7
bin/foreman
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
3
example/Procfile
Normal file
3
example/Procfile
Normal file
@@ -0,0 +1,3 @@
|
||||
neverdie ./never_die
|
||||
diealot ./die_alot
|
||||
error ./error
|
||||
4
example/die_alot
Executable file
4
example/die_alot
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
sleep 2
|
||||
exit 0
|
||||
4
example/error
Executable file
4
example/error
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
sleep 10
|
||||
raise "Dying"
|
||||
5
example/never_die
Executable file
5
example/never_die
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
while true
|
||||
sleep 5
|
||||
end
|
||||
6
lib/foreman.rb
Normal file
6
lib/foreman.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.0.1"
|
||||
|
||||
end
|
||||
|
||||
35
lib/foreman/cli.rb
Normal file
35
lib/foreman/cli.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
require "foreman"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
desc "start [PROCFILE]", "Run the app described in PROCFILE"
|
||||
|
||||
def start(procfile="Procfile")
|
||||
error "#{procfile} does not exist." unless File.exist?(procfile)
|
||||
Foreman::Engine.new(procfile).start
|
||||
end
|
||||
|
||||
desc "export NAME [PROCFILE] [FORMAT]", "Export the app described in PROCFILE as NAME to another FORMAT"
|
||||
|
||||
def export(name, procfile="Procfile", format="upstart")
|
||||
error "#{procfile} does not exist." unless File.exist?(procfile)
|
||||
|
||||
formatter = case format
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
else error "Unknown export format: #{format}"
|
||||
end
|
||||
|
||||
formatter.new(Foreman::Engine.new(procfile)).export(name)
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
end
|
||||
87
lib/foreman/engine.rb
Normal file
87
lib/foreman/engine.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
require "foreman"
|
||||
require "foreman/process"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :directory
|
||||
|
||||
def initialize(procfile)
|
||||
@procfile = File.read(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
end
|
||||
|
||||
def processes
|
||||
@processes ||= begin
|
||||
procfile.split("\n").inject({}) do |hash, line|
|
||||
next if line.strip == ""
|
||||
process = Foreman::Process.new(*line.split(" ", 2))
|
||||
hash.update(process.name => process)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes.each do |name, process|
|
||||
fork process
|
||||
end
|
||||
|
||||
trap("TERM") { kill_and_exit("TERM") }
|
||||
trap("INT") { kill_and_exit("INT") }
|
||||
trap("INFO") { print_info }
|
||||
|
||||
while true
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "exited with code #{status}", process
|
||||
fork process
|
||||
end
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def fork(process)
|
||||
pid = Process.fork do
|
||||
proctitle "ruby: foreman #{process.name}"
|
||||
|
||||
Dir.chdir directory do
|
||||
system process.command
|
||||
exit $?.exitstatus || 255
|
||||
end
|
||||
end
|
||||
|
||||
info "started with pid #{pid}", process
|
||||
running_processes[pid] = process
|
||||
end
|
||||
|
||||
def kill_and_exit(signal="TERM")
|
||||
info "termination requested"
|
||||
running_processes.each do |pid, process|
|
||||
info "killing pid #{pid}", process
|
||||
Process.kill(signal, pid)
|
||||
end
|
||||
exit 0
|
||||
end
|
||||
|
||||
def info(message, process=nil)
|
||||
puts "[foreman] [#{Time.now.utc}] [#{process ? process.name : "system"}] #{message}"
|
||||
end
|
||||
|
||||
def print_info
|
||||
info "currently running processes:"
|
||||
running_processes.each do |pid, process|
|
||||
info "pid #{pid}", process
|
||||
end
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@running_processes ||= {}
|
||||
end
|
||||
|
||||
def proctitle(title)
|
||||
$0 = title
|
||||
end
|
||||
|
||||
end
|
||||
6
lib/foreman/export.rb
Normal file
6
lib/foreman/export.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
require "foreman"
|
||||
|
||||
module Foreman::Export
|
||||
end
|
||||
|
||||
require "foreman/export/upstart"
|
||||
63
lib/foreman/export/upstart.rb
Normal file
63
lib/foreman/export/upstart.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart
|
||||
|
||||
attr_reader :engine
|
||||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
end
|
||||
|
||||
def export(name)
|
||||
FileUtils.mkdir_p "/etc/foreman"
|
||||
FileUtils.mkdir_p "/etc/init"
|
||||
|
||||
write_file "/etc/foreman/#{name}.conf", <<-UPSTART_CONFIG
|
||||
#{name}_processes="#{engine.processes.keys.join(' ')}"
|
||||
#{engine.processes.keys.map { |k| "#{name}_#{k}=\"1\"" }.join("\n")}
|
||||
UPSTART_CONFIG
|
||||
|
||||
write_file "/etc/init/#{name}.conf", <<-UPSTART_MASTER
|
||||
pre-start script
|
||||
|
||||
bash << "EOF"
|
||||
mkdir -p /var/log/#{name}
|
||||
|
||||
if [ -f /etc/foreman/#{name}.conf ]; then
|
||||
source /etc/foreman/#{name}.conf
|
||||
fi
|
||||
|
||||
for process in $( echo "$#{name}_processes" ); do
|
||||
process_count_config="#{name}_$process"
|
||||
process_count=${!process_count_config}
|
||||
|
||||
for ((i=1; i<=${process_count:=1}; i+=1)); do
|
||||
start #{name}-$process NUM=$i
|
||||
done
|
||||
done
|
||||
EOF
|
||||
|
||||
end script
|
||||
UPSTART_MASTER
|
||||
|
||||
engine.processes.values.each do |process|
|
||||
write_file "/etc/init/#{name}-#{process.name}.conf", <<-UPSTART_CHILD
|
||||
instance $NUM
|
||||
stop on stopping #{name}
|
||||
respawn
|
||||
|
||||
chdir #{engine.directory}
|
||||
exec #{process.command} 2>&1 > /var/log/#{name}/#{process.name}.log
|
||||
UPSTART_CHILD
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write_file(filename, contents)
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
13
lib/foreman/process.rb
Normal file
13
lib/foreman/process.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :command
|
||||
|
||||
def initialize(name, command)
|
||||
@name = name
|
||||
@command = command
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user