foreman 0.47.0 → 0.48.0.pre3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/taskman +8 -0
- data/data/example/Procfile +4 -3
- data/data/example/spawnee +14 -0
- data/data/example/spawner +7 -0
- data/data/export/bluepill/master.pill.erb +10 -10
- data/data/export/launchd/launchd.plist.erb +3 -3
- data/data/export/runit/log/run.erb +7 -0
- data/data/export/runit/run.erb +2 -2
- data/data/export/supervisord/app.conf.erb +12 -12
- data/data/export/upstart/master.conf.erb +2 -2
- data/data/export/upstart/process.conf.erb +3 -3
- data/lib/foreman/cli.rb +57 -22
- data/lib/foreman/engine/cli.rb +103 -0
- data/lib/foreman/engine.rb +208 -148
- data/lib/foreman/env.rb +27 -0
- data/lib/foreman/export/base.rb +58 -35
- data/lib/foreman/export/bluepill.rb +3 -17
- data/lib/foreman/export/inittab.rb +8 -11
- data/lib/foreman/export/launchd.rb +4 -16
- data/lib/foreman/export/runit.rb +14 -39
- data/lib/foreman/export/supervisord.rb +3 -13
- data/lib/foreman/export/upstart.rb +9 -27
- data/lib/foreman/export.rb +0 -1
- data/lib/foreman/process.rb +56 -67
- data/lib/foreman/procfile.rb +59 -25
- data/lib/foreman/version.rb +1 -1
- data/man/foreman.1 +4 -0
- data/spec/foreman/cli_spec.rb +46 -150
- data/spec/foreman/engine_spec.rb +46 -80
- data/spec/foreman/export/base_spec.rb +4 -7
- data/spec/foreman/export/bluepill_spec.rb +7 -6
- data/spec/foreman/export/inittab_spec.rb +7 -7
- data/spec/foreman/export/launchd_spec.rb +4 -7
- data/spec/foreman/export/runit_spec.rb +12 -17
- data/spec/foreman/export/supervisord_spec.rb +7 -56
- data/spec/foreman/export/upstart_spec.rb +18 -23
- data/spec/foreman/process_spec.rb +27 -124
- data/spec/foreman/procfile_spec.rb +26 -16
- data/spec/resources/Procfile +4 -0
- data/spec/resources/bin/echo +2 -0
- data/spec/resources/bin/env +2 -0
- data/spec/resources/bin/test +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +4 -4
- data/spec/resources/export/bluepill/app.pill +4 -4
- data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
- data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
- data/spec/spec_helper.rb +58 -6
- metadata +24 -22
- data/data/export/runit/log_run.erb +0 -7
- data/lib/foreman/color.rb +0 -40
- data/lib/foreman/procfile_entry.rb +0 -26
- data/lib/foreman/utils.rb +0 -18
- data/spec/foreman/color_spec.rb +0 -31
- data/spec/foreman/procfile_entry_spec.rb +0 -13
- data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
- data/spec/resources/export/supervisord/app-env.conf +0 -21
- data/spec/resources/export/supervisord/app.conf +0 -24
- /data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
- /data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
- /data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
data/bin/taskman
ADDED
data/data/example/Procfile
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
ticker:
|
|
2
|
-
error:
|
|
3
|
-
utf8:
|
|
1
|
+
ticker: ruby ./ticker $PORT
|
|
2
|
+
error: ruby ./error
|
|
3
|
+
utf8: ruby ./utf8
|
|
4
|
+
spawner: ./spawner
|
|
@@ -3,25 +3,25 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
|
3
3
|
app.uid = "<%= user %>"
|
|
4
4
|
app.gid = "<%= user %>"
|
|
5
5
|
|
|
6
|
-
<% engine.
|
|
7
|
-
<% 1.upto(
|
|
8
|
-
<% port = engine.port_for(process, num
|
|
9
|
-
app.process("<%=
|
|
10
|
-
process.start_command = "<%= process.command
|
|
6
|
+
<% engine.each_process do |name, process| %>
|
|
7
|
+
<% 1.upto(engine.formation[name]) do |num| %>
|
|
8
|
+
<% port = engine.port_for(process, num) %>
|
|
9
|
+
app.process("<%= name %>-<%= num %>") do |process|
|
|
10
|
+
process.start_command = "<%= process.command %>"
|
|
11
11
|
|
|
12
|
-
process.working_dir = "<%= engine.
|
|
12
|
+
process.working_dir = "<%= engine.root %>"
|
|
13
13
|
process.daemonize = true
|
|
14
|
-
process.environment =
|
|
14
|
+
process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %>
|
|
15
15
|
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
|
16
16
|
process.stop_grace_time = 45.seconds
|
|
17
17
|
|
|
18
|
-
process.stdout = process.stderr = "<%=
|
|
18
|
+
process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log"
|
|
19
19
|
|
|
20
20
|
process.monitor_children do |children|
|
|
21
|
-
children.stop_command "kill
|
|
21
|
+
children.stop_command "kill {{PID}}"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
process.group = "<%= app %>-<%=
|
|
24
|
+
process.group = "<%= app %>-<%= name %>"
|
|
25
25
|
end
|
|
26
26
|
<% end %>
|
|
27
27
|
<% end %>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<plist version="1.0">
|
|
4
4
|
<dict>
|
|
5
5
|
<key>Label</key>
|
|
6
|
-
<string><%= "#{app}-#{
|
|
6
|
+
<string><%= "#{app}-#{name}-#{num}" %></string>
|
|
7
7
|
<key>ProgramArguments</key>
|
|
8
8
|
<array>
|
|
9
9
|
<string><%= process.command %></string>
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
<key>RunAtLoad</key>
|
|
14
14
|
<true/>
|
|
15
15
|
<key>StandardErrorPath</key>
|
|
16
|
-
<string><%=
|
|
16
|
+
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
|
|
17
17
|
<key>UserName</key>
|
|
18
18
|
<string><%= user %></string>
|
|
19
19
|
<key>WorkingDirectory</key>
|
|
20
|
-
<string><%= engine.
|
|
20
|
+
<string><%= engine.root %></string>
|
|
21
21
|
</dict>
|
|
22
22
|
</plist>
|
data/data/export/runit/run.erb
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
cd <%= engine.
|
|
3
|
-
exec chpst -u <%= user %> -e <%=
|
|
2
|
+
cd <%= engine.root %>
|
|
3
|
+
exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
<%
|
|
2
2
|
app_names = []
|
|
3
|
-
engine.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
app_names <<
|
|
3
|
+
engine.each_process do |name, process|
|
|
4
|
+
1.upto(engine.formation[name]) do |num|
|
|
5
|
+
port = engine.port_for(process, num)
|
|
6
|
+
full_name = "#{app}-#{name}-#{num}"
|
|
7
|
+
environment = engine.env.merge("PORT" => port.to_s).map do |key, value|
|
|
8
|
+
"#{key}=#{shell_quote(value)}"
|
|
9
|
+
end
|
|
10
|
+
app_names << full_name
|
|
11
11
|
%>
|
|
12
|
-
[program:<%=
|
|
12
|
+
[program:<%= full_name %>]
|
|
13
13
|
command=<%= process.command %>
|
|
14
14
|
autostart=true
|
|
15
15
|
autorestart=true
|
|
16
16
|
stopsignal=QUIT
|
|
17
|
-
stdout_logfile=<%=
|
|
18
|
-
stderr_logfile=<%=
|
|
17
|
+
stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
|
|
18
|
+
stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
|
|
19
19
|
user=<%= user %>
|
|
20
|
-
directory=<%= engine.
|
|
20
|
+
directory=<%= engine.root %>
|
|
21
21
|
environment=<%= environment.join(',') %><%
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
start on starting <%= app %>-<%=
|
|
2
|
-
stop on stopping <%= app %>-<%=
|
|
1
|
+
start on starting <%= app %>-<%= name %>
|
|
2
|
+
stop on stopping <%= app %>-<%= name %>
|
|
3
3
|
respawn
|
|
4
4
|
|
|
5
|
-
exec su - <%= user %> -c 'cd <%= engine.
|
|
5
|
+
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'
|
data/lib/foreman/cli.rb
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
require "foreman"
|
|
2
2
|
require "foreman/helpers"
|
|
3
3
|
require "foreman/engine"
|
|
4
|
+
require "foreman/engine/cli"
|
|
4
5
|
require "foreman/export"
|
|
6
|
+
require "foreman/version"
|
|
5
7
|
require "shellwords"
|
|
6
|
-
require "thor"
|
|
7
8
|
require "yaml"
|
|
9
|
+
require "thor"
|
|
8
10
|
|
|
9
11
|
class Foreman::CLI < Thor
|
|
12
|
+
|
|
10
13
|
include Foreman::Helpers
|
|
11
14
|
|
|
15
|
+
map ["-v", "--version"] => :version
|
|
16
|
+
|
|
12
17
|
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
|
18
|
+
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
|
13
19
|
|
|
14
20
|
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
method_option :
|
|
20
|
-
method_option :port, :type => :numeric, :aliases => "-p"
|
|
21
|
-
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
|
22
|
+
method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled"
|
|
23
|
+
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
|
24
|
+
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
|
|
25
|
+
method_option :port, :type => :numeric, :aliases => "-p"
|
|
22
26
|
|
|
23
27
|
class << self
|
|
24
28
|
# Hackery. Take the run method away from Thor so that we can redefine it.
|
|
25
29
|
def is_thor_reserved_word?(word, type)
|
|
26
|
-
return false if word ==
|
|
30
|
+
return false if word == "run"
|
|
27
31
|
super
|
|
28
32
|
end
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def start(process=nil)
|
|
32
36
|
check_procfile!
|
|
33
|
-
|
|
37
|
+
load_environment!
|
|
38
|
+
engine.load_procfile(procfile)
|
|
39
|
+
engine.options[:formation] = "#{process}=1" if process
|
|
34
40
|
engine.start
|
|
35
41
|
end
|
|
36
42
|
|
|
@@ -46,6 +52,8 @@ class Foreman::CLI < Thor
|
|
|
46
52
|
|
|
47
53
|
def export(format, location=nil)
|
|
48
54
|
check_procfile!
|
|
55
|
+
load_environment!
|
|
56
|
+
engine.load_procfile(procfile)
|
|
49
57
|
formatter = Foreman::Export.formatter(format)
|
|
50
58
|
formatter.new(location, engine, options).export
|
|
51
59
|
rescue Foreman::Export::Exception => ex
|
|
@@ -56,16 +64,19 @@ class Foreman::CLI < Thor
|
|
|
56
64
|
|
|
57
65
|
def check
|
|
58
66
|
check_procfile!
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
engine.load_procfile(procfile)
|
|
68
|
+
error "no processes defined" unless engine.processes.length > 0
|
|
69
|
+
puts "valid procfile detected (#{engine.process_names.join(', ')})"
|
|
61
70
|
end
|
|
62
71
|
|
|
63
72
|
desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
|
|
64
73
|
|
|
74
|
+
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
|
75
|
+
|
|
65
76
|
def run(*args)
|
|
66
|
-
|
|
77
|
+
load_environment!
|
|
67
78
|
begin
|
|
68
|
-
exec args.shelljoin
|
|
79
|
+
exec engine.env, args.shelljoin
|
|
69
80
|
rescue Errno::EACCES
|
|
70
81
|
error "not executable: #{args.first}"
|
|
71
82
|
rescue Errno::ENOENT
|
|
@@ -73,33 +84,57 @@ class Foreman::CLI < Thor
|
|
|
73
84
|
end
|
|
74
85
|
end
|
|
75
86
|
|
|
87
|
+
desc "version", "Display Foreman gem version"
|
|
88
|
+
|
|
89
|
+
def version
|
|
90
|
+
puts Foreman::VERSION
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
no_tasks do
|
|
94
|
+
def engine
|
|
95
|
+
@engine ||= begin
|
|
96
|
+
engine_class = Foreman::Engine::CLI
|
|
97
|
+
engine = engine_class.new(options)
|
|
98
|
+
engine
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
76
103
|
private ######################################################################
|
|
77
104
|
|
|
105
|
+
def error(message)
|
|
106
|
+
puts "ERROR: #{message}"
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
109
|
+
|
|
78
110
|
def check_procfile!
|
|
79
111
|
error("#{procfile} does not exist.") unless File.exist?(procfile)
|
|
80
112
|
end
|
|
81
113
|
|
|
82
|
-
def
|
|
83
|
-
|
|
114
|
+
def load_environment!
|
|
115
|
+
if options[:env]
|
|
116
|
+
options[:env].split(",").each do |file|
|
|
117
|
+
engine.load_env file
|
|
118
|
+
end
|
|
119
|
+
else
|
|
120
|
+
default_env = File.join(engine.root, ".env")
|
|
121
|
+
engine.load_env default_env if File.exists?(default_env)
|
|
122
|
+
end
|
|
84
123
|
end
|
|
85
124
|
|
|
86
125
|
def procfile
|
|
87
126
|
case
|
|
88
127
|
when options[:procfile] then options[:procfile]
|
|
89
|
-
when options[:
|
|
128
|
+
when options[:root] then File.expand_path(File.join(options[:app_root], "Procfile"))
|
|
90
129
|
else "Procfile"
|
|
91
130
|
end
|
|
92
131
|
end
|
|
93
132
|
|
|
94
|
-
def error(message)
|
|
95
|
-
puts "ERROR: #{message}"
|
|
96
|
-
exit 1
|
|
97
|
-
end
|
|
98
|
-
|
|
99
133
|
def options
|
|
100
134
|
original_options = super
|
|
101
135
|
return original_options unless File.exists?(".foreman")
|
|
102
|
-
defaults = YAML::load_file(".foreman") || {}
|
|
136
|
+
defaults = ::YAML::load_file(".foreman") || {}
|
|
103
137
|
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
|
104
138
|
end
|
|
139
|
+
|
|
105
140
|
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require "foreman/engine"
|
|
2
|
+
|
|
3
|
+
class Foreman::Engine::CLI < Foreman::Engine
|
|
4
|
+
|
|
5
|
+
module Color
|
|
6
|
+
|
|
7
|
+
ANSI = {
|
|
8
|
+
:reset => 0,
|
|
9
|
+
:black => 30,
|
|
10
|
+
:red => 31,
|
|
11
|
+
:green => 32,
|
|
12
|
+
:yellow => 33,
|
|
13
|
+
:blue => 34,
|
|
14
|
+
:magenta => 35,
|
|
15
|
+
:cyan => 36,
|
|
16
|
+
:white => 37,
|
|
17
|
+
:bright_black => 30,
|
|
18
|
+
:bright_red => 31,
|
|
19
|
+
:bright_green => 32,
|
|
20
|
+
:bright_yellow => 33,
|
|
21
|
+
:bright_blue => 34,
|
|
22
|
+
:bright_magenta => 35,
|
|
23
|
+
:bright_cyan => 36,
|
|
24
|
+
:bright_white => 37,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def self.enable(io, force=false)
|
|
28
|
+
io.extend(self)
|
|
29
|
+
@@color_force = force
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def color?
|
|
33
|
+
return true if @@color_force
|
|
34
|
+
return false unless self.respond_to?(:isatty)
|
|
35
|
+
self.isatty && ENV["TERM"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def color(name)
|
|
39
|
+
return "" unless color?
|
|
40
|
+
return "" unless ansi = ANSI[name.to_sym]
|
|
41
|
+
"\e[#{ansi}m"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
|
|
47
|
+
intense_green intense_magenta intense_red, intense_blue )
|
|
48
|
+
|
|
49
|
+
def startup
|
|
50
|
+
@colors = map_colors
|
|
51
|
+
proctitle "foreman: master"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def output(name, data)
|
|
55
|
+
data.to_s.chomp.split("\n").each do |message|
|
|
56
|
+
Color.enable($stdout, options[:color]) unless $stdout.respond_to?(:color?)
|
|
57
|
+
output = ""
|
|
58
|
+
output += $stdout.color(@colors[name.split(".").first].to_sym)
|
|
59
|
+
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
|
60
|
+
output += $stdout.color(:reset)
|
|
61
|
+
output += message
|
|
62
|
+
$stdout.puts output
|
|
63
|
+
$stdout.flush
|
|
64
|
+
end
|
|
65
|
+
rescue Errno::EPIPE
|
|
66
|
+
terminate_gracefully
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def shutdown
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def name_padding
|
|
75
|
+
@name_padding ||= begin
|
|
76
|
+
index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
|
|
77
|
+
name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
|
|
78
|
+
[ 6, name_padding ].max
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def pad_process_name(name)
|
|
83
|
+
name.ljust(name_padding, " ")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def map_colors
|
|
87
|
+
colors = Hash.new("white")
|
|
88
|
+
@names.values.each_with_index do |name, index|
|
|
89
|
+
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
|
|
90
|
+
end
|
|
91
|
+
colors["system"] = "intense_white"
|
|
92
|
+
colors
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def proctitle(title)
|
|
96
|
+
$0 = title
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def termtitle(title)
|
|
100
|
+
printf("\033]0;#{title}\007") unless Foreman.windows?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|