Skip to content

Commit

Permalink
massive refactoring for programmatic control and stability
Browse files Browse the repository at this point in the history
  • Loading branch information
ddollar committed Jun 11, 2012
1 parent f41cc55 commit 51a7049
Show file tree
Hide file tree
Showing 65 changed files with 961 additions and 1,294 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.bundle
/.rbenv-version
/.yardoc
/coverage
/example/log/*
/man/*.html
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ group :development do
gem 'rspec', '~> 2.0'
gem "simplecov", :require => false
gem 'timecop'
gem 'yard'
end
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ GEM
timecop (0.3.5)
win32console (1.3.0-x86-mingw32)
xml-simple (1.0.15)
yard (0.8.2)

PLATFORMS
java
Expand All @@ -61,3 +62,4 @@ DEPENDENCIES
simplecov
timecop
win32console (~> 1.3.0)
yard
7 changes: 4 additions & 3 deletions data/example/Procfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ticker: ruby ./ticker $PORT
error: ruby ./error
utf8: ruby ./utf8
ticker: ruby ./ticker $PORT
error: ruby ./error
utf8: ruby ./utf8
spawner: ./spawner
14 changes: 14 additions & 0 deletions data/example/spawnee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

NAME="$1"

sigterm() {
echo "$NAME: got sigterm"
}

#trap sigterm SIGTERM

while true; do
echo "$NAME: ping"
sleep 1
done
7 changes: 7 additions & 0 deletions data/example/spawner
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

./spawnee A &
./spawnee B &
./spawnee C &

wait
20 changes: 10 additions & 10 deletions data/export/bluepill/master.pill.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
app.uid = "<%= user %>"
app.gid = "<%= user %>"

<% engine.procfile.entries.each do |process| %>
<% 1.upto(concurrency[process.name]) do |num| %>
<% port = engine.port_for(process, num, self.port) %>
app.process("<%= process.name %>-<%=num%>") do |process|
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
<% engine.each_process do |name, process| %>
<% 1.upto(engine.formation[name]) do |num| %>
<% port = engine.port_for(process, num) %>
app.process("<%= name %>-<%= num %>") do |process|
process.start_command = "<%= process.command %>"

process.working_dir = "<%= engine.directory %>"
process.working_dir = "<%= engine.root %>"
process.daemonize = true
process.environment = {"PORT" => "<%= port %>"<% engine.environment.each_pair do |var,env| %> , "<%= var.upcase %>" => "<%= env %>" <% end %>}
process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %>
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds

process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log"

process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end

process.group = "<%= app %>-<%= process.name %>"
process.group = "<%= app %>-<%= name %>"
end
<% end %>
<% end %>
Expand Down
6 changes: 3 additions & 3 deletions data/export/launchd/launchd.plist.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string><%= "#{app}-#{process.name}-#{num}" %></string>
<string><%= "#{app}-#{name}-#{num}" %></string>
<key>ProgramArguments</key>
<array>
<string><%= process.command %></string>
Expand All @@ -13,10 +13,10 @@
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string><%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log</string>
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
<key>UserName</key>
<string><%= user %></string>
<key>WorkingDirectory</key>
<string><%= engine.directory %></string>
<string><%= engine.root %></string>
</dict>
</plist>
7 changes: 7 additions & 0 deletions data/export/runit/log/run.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
set -e

LOG=<%= log %>/<%= name %>-<%= num %>

test -d "$LOG" || mkdir -p m2750 "$LOG" && chown <%= user %> "$LOG"
exec chpst -u <%= user %> svlogd "$LOG"
7 changes: 0 additions & 7 deletions data/export/runit/log_run.erb

This file was deleted.

4 changes: 2 additions & 2 deletions data/export/runit/run.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/sh
cd <%= engine.directory %>
exec chpst -u <%= user %> -e <%= process_env_directory %> <%= process.command %>
cd <%= engine.root %>
exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>
24 changes: 12 additions & 12 deletions data/export/supervisord/app.conf.erb
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
<%
app_names = []
engine.procfile.entries.each do |process|
next if (conc = self.concurrency[process.name]) < 1
1.upto(self.concurrency[process.name]) do |num|
port = engine.port_for(process, num, self.port)
name = if (conc > 1); "#{process.name}-#{num}" else process.name; end
environment = (engine.environment.keys.sort.map{ |var| %{#{var.upcase}="#{engine.environment[var]}"} } + [%{PORT="#{port}"}])
app_name = "#{app}-#{name}"
app_names << app_name
engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
full_name = "#{app}-#{name}-#{num}"
environment = engine.env.merge("PORT" => port.to_s).map do |key, value|
"#{key}=#{shell_quote(value)}"
end
app_names << full_name
%>
[program:<%= app_name %>]
[program:<%= full_name %>]
command=<%= process.command %>
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=<%= log_root %>/<%=process.name%>-<%=num%>-out.log
stderr_logfile=<%= log_root %>/<%=process.name%>-<%=num%>-err.log
stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
user=<%= user %>
directory=<%= engine.directory %>
directory=<%= engine.root %>
environment=<%= environment.join(',') %><%
end
end
Expand Down
4 changes: 2 additions & 2 deletions data/export/upstart/master.conf.erb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pre-start script

bash << "EOF"
mkdir -p <%= log_root %>
chown -R <%= user %> <%= log_root %>
mkdir -p <%= log %>
chown -R <%= user %> <%= log %>
EOF

end script
6 changes: 3 additions & 3 deletions data/export/upstart/process.conf.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
start on starting <%= app %>-<%= process.name %>
stop on stopping <%= app %>-<%= process.name %>
start on starting <%= app %>-<%= name %>
stop on stopping <%= app %>-<%= name %>
respawn

exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
exec su - <%= user %> -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1'
77 changes: 46 additions & 31 deletions lib/foreman/cli.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
require "foreman"
require "foreman/helpers"
require "foreman/engine"
require "foreman/tmux_engine"
require "foreman/engine/cli"
require "foreman/export"
require "shellwords"
require "thor"
require "yaml"

class Foreman::CLI < Thor

include Foreman::Helpers

class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
class_option :tmux, :type => :boolean, :aliases => "-t", :desc => "Run in tmux session"
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"

desc "start [PROCESS]", "Start the application (or a specific PROCESS)"

class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
class_option :app_root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"

method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p"
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
method_option :port, :type => :numeric, :aliases => "-p"

class << self
# Hackery. Take the run method away from Thor so that we can redefine it.
def is_thor_reserved_word?(word, type)
return false if word == 'run'
return false if word == "run"
super
end
end

def start(process=nil)
check_procfile!
engine.options[:concurrency] = "#{process}=1" if process
load_environment!
engine.load_procfile(procfile)
engine.options[:formation] = "#{process}=1" if process
engine.start
end

Expand All @@ -48,6 +47,8 @@ def start(process=nil)

def export(format, location=nil)
check_procfile!
load_environment!
engine.load_procfile(procfile)
formatter = Foreman::Export.formatter(format)
formatter.new(location, engine, options).export
rescue Foreman::Export::Exception => ex
Expand All @@ -58,61 +59,75 @@ def export(format, location=nil)

def check
check_procfile!
error "no processes defined" unless engine.procfile.entries.length > 0
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
engine.load_procfile(procfile)
error "no processes defined" unless engine.processes.length > 0
puts "valid procfile detected (#{engine.process_names.join(', ')})"
end

desc "run COMMAND [ARGS...]", "Run a command using your application's environment"

method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"

def run(*args)
engine.apply_environment!
load_environment!
begin
exec args.shelljoin
exec engine.env, args.shelljoin
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
end
end

class << self
def new_engine(procfile, options)
@engine_class ||= options[:tmux] ? Foreman::TmuxEngine : Foreman::Engine
@engine_class.new(procfile, options)
end

def engine_class=(klass)
@engine_class = klass
no_tasks do
def engine
@engine ||= begin
engine_class = Foreman::Engine::CLI
engine = engine_class.new(
:formation => options[:formation],
:port => options[:port],
:root => options[:root]
)
engine
end
end
end

private ######################################################################

def error(message)
puts "ERROR: #{message}"
exit 1
end

def check_procfile!
error("#{procfile} does not exist.") unless File.exist?(procfile)
end

def engine
@engine ||= self.class.new_engine(procfile, options)
def load_environment!
if options[:env]
options[:env].split(",").each do |file|
engine.load_env file
end
else
default_env = File.join(engine.root, ".env")
engine.load_env default_env if File.exists?(default_env)
end
end

def procfile
case
when options[:procfile] then options[:procfile]
when options[:app_root] then File.expand_path(File.join(options[:app_root], "Procfile"))
when options[:root] then File.expand_path(File.join(options[:app_root], "Procfile"))
else "Procfile"
end
end

def error(message)
puts "ERROR: #{message}"
exit 1
end

def options
original_options = super
return original_options unless File.exists?(".foreman")
defaults = YAML::load_file(".foreman") || {}
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
end

end
40 changes: 0 additions & 40 deletions lib/foreman/color.rb

This file was deleted.

Loading

0 comments on commit 51a7049

Please sign in to comment.