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