foreman 0.15.0 → 0.27.0
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/README.markdown +39 -1
- data/data/example/Procfile +2 -2
- data/data/example/error +2 -0
- data/data/example/ticker +9 -1
- data/data/export/bluepill/master.pill.erb +27 -0
- data/data/export/runit/log_run.erb +7 -0
- data/data/export/runit/run.erb +3 -0
- data/data/export/upstart/process.conf.erb +1 -2
- data/lib/foreman/cli.rb +12 -9
- data/lib/foreman/distribution.rb +9 -0
- data/lib/foreman/engine.rb +96 -74
- data/lib/foreman/export/base.rb +8 -2
- data/lib/foreman/export/bluepill.rb +28 -0
- data/lib/foreman/export/inittab.rb +2 -2
- data/lib/foreman/export/runit.rb +60 -0
- data/lib/foreman/export/upstart.rb +7 -5
- data/lib/foreman/export.rb +3 -0
- data/lib/foreman/procfile.rb +37 -0
- data/lib/foreman/utils.rb +1 -1
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +5 -0
- data/man/foreman.1 +27 -3
- data/spec/foreman/cli_spec.rb +10 -1
- data/spec/foreman/engine_spec.rb +59 -24
- data/spec/foreman/export/bluepill_spec.rb +20 -0
- data/spec/foreman/export/runit_spec.rb +35 -0
- data/spec/foreman/export/upstart_spec.rb +43 -9
- data/spec/foreman_spec.rb +22 -0
- data/spec/resources/export/bluepill/app.pill +65 -0
- data/spec/resources/export/runit/app-alpha-1-log-run +7 -0
- data/spec/resources/export/runit/app-alpha-1-run +3 -0
- data/spec/resources/export/runit/app-alpha-2-log-run +7 -0
- data/spec/resources/export/runit/app-alpha-2-run +3 -0
- data/spec/resources/export/runit/app-bravo-1-log-run +7 -0
- data/spec/resources/export/runit/app-bravo-1-run +3 -0
- data/spec/resources/export/upstart/app-alpha-1.conf +5 -0
- data/spec/resources/export/upstart/app-alpha-2.conf +5 -0
- data/spec/resources/export/upstart/app-alpha.conf +2 -0
- data/spec/resources/export/upstart/app-bravo-1.conf +5 -0
- data/spec/resources/export/upstart/app-bravo.conf +2 -0
- data/spec/resources/export/upstart/app.conf +8 -0
- data/spec/spec_helper.rb +16 -5
- metadata +60 -124
- data/spec/resources/export/upstart/foreman-alpha-1.conf +0 -6
- data/spec/resources/export/upstart/foreman-alpha-2.conf +0 -6
- data/spec/resources/export/upstart/foreman-alpha.conf +0 -2
- data/spec/resources/export/upstart/foreman-bravo-1.conf +0 -6
- data/spec/resources/export/upstart/foreman.bravo.conf +0 -2
- data/spec/resources/export/upstart/foreman.conf +0 -8
data/README.markdown
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
1
|
# Foreman
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
* Rubygems
|
|
6
|
+
|
|
7
|
+
gem install foreman
|
|
8
|
+
|
|
9
|
+
* OSX
|
|
10
|
+
|
|
11
|
+
http://assets.foreman.io/foreman/foreman.pkg
|
|
12
|
+
|
|
13
|
+
* Standalone Tarball
|
|
14
|
+
|
|
15
|
+
http://assets.foreman.io/foreman/foreman.tgz
|
|
16
|
+
|
|
17
|
+
## Description
|
|
4
18
|
|
|
5
19
|
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
|
6
20
|
|
|
21
|
+
## Manual
|
|
22
|
+
|
|
7
23
|
See the [man page](http://ddollar.github.com/foreman) for usage.
|
|
8
24
|
|
|
25
|
+
## Authorship
|
|
26
|
+
|
|
27
|
+
Created by David Dollar
|
|
28
|
+
|
|
29
|
+
Patches contributed by:
|
|
30
|
+
|
|
31
|
+
* Adam Wiggins
|
|
32
|
+
* Dan Peterson
|
|
33
|
+
* Hunter Nield
|
|
34
|
+
* Jay Zeschin
|
|
35
|
+
* Keith Rarick
|
|
36
|
+
* Khaja Minhajuddin
|
|
37
|
+
* Matt Haynes
|
|
38
|
+
* Michael van Rooijen
|
|
39
|
+
* Mike Javorski
|
|
40
|
+
* Nathan L Smith
|
|
41
|
+
* Nick Zadrozny
|
|
42
|
+
* Ricardo Chimal, Jr
|
|
43
|
+
* Thom May
|
|
44
|
+
* clifff
|
|
45
|
+
* Greg Reinacker
|
|
46
|
+
|
|
9
47
|
## License
|
|
10
48
|
|
|
11
49
|
MIT
|
data/data/example/Procfile
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
ticker: ./ticker $PORT
|
|
2
|
-
error
|
|
1
|
+
ticker: ruby ./ticker $PORT
|
|
2
|
+
error: ruby ./error
|
data/data/example/error
CHANGED
data/data/example/ticker
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
$stdout.sync = true
|
|
4
|
+
|
|
5
|
+
%w( SIGINT SIGTERM ).each do |signal|
|
|
6
|
+
trap(signal) do
|
|
7
|
+
puts "received #{signal} but i'm ignoring it!"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
3
11
|
while true
|
|
4
|
-
puts "tick: #{ARGV.inspect}"
|
|
12
|
+
puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
|
|
5
13
|
sleep 1
|
|
6
14
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
|
2
|
+
|
|
3
|
+
app.uid = "<%= user %>"
|
|
4
|
+
app.gid = "<%= user %>"
|
|
5
|
+
|
|
6
|
+
<% engine.processes.each do |process| %>
|
|
7
|
+
<% 1.upto(concurrency[process.name]) do |num| %>
|
|
8
|
+
<% port = engine.port_for(process, num, options[:port]) %>
|
|
9
|
+
app.process("<%= process.name %>-<%=num%>") do |process|
|
|
10
|
+
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
|
11
|
+
|
|
12
|
+
process.working_dir = "<%= engine.directory %>"
|
|
13
|
+
process.daemonize = true
|
|
14
|
+
process.environment = {"PORT" => "<%= port %>"}
|
|
15
|
+
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
|
16
|
+
|
|
17
|
+
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
|
|
18
|
+
|
|
19
|
+
process.monitor_children do |children|
|
|
20
|
+
children.stop_command "kill -QUIT {{PID}}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
process.group = "<%= app %>-<%= process.name %>"
|
|
24
|
+
end
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
end
|
|
@@ -2,5 +2,4 @@ start on starting <%= app %>-<%= process.name %>
|
|
|
2
2
|
stop on stopping <%= app %>-<%= process.name %>
|
|
3
3
|
respawn
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
exec su <%= user %> -c 'export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
|
5
|
+
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
data/lib/foreman/cli.rb
CHANGED
|
@@ -10,17 +10,17 @@ class Foreman::CLI < Thor
|
|
|
10
10
|
|
|
11
11
|
desc "start [PROCESS]", "Start the application, or a specific process"
|
|
12
12
|
|
|
13
|
+
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
|
13
14
|
method_option :port, :type => :numeric, :aliases => "-p"
|
|
14
|
-
method_option :concurrency, :type => :string, :aliases => "-c",
|
|
15
|
-
:banner => '"alpha=5,bar=3"'
|
|
15
|
+
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
|
16
16
|
|
|
17
17
|
def start(process=nil)
|
|
18
18
|
check_procfile!
|
|
19
19
|
|
|
20
20
|
if process
|
|
21
|
-
engine.execute(process
|
|
21
|
+
engine.execute(process)
|
|
22
22
|
else
|
|
23
|
-
engine.start
|
|
23
|
+
engine.start
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -28,8 +28,10 @@ class Foreman::CLI < Thor
|
|
|
28
28
|
|
|
29
29
|
method_option :app, :type => :string, :aliases => "-a"
|
|
30
30
|
method_option :log, :type => :string, :aliases => "-l"
|
|
31
|
+
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
|
31
32
|
method_option :port, :type => :numeric, :aliases => "-p"
|
|
32
33
|
method_option :user, :type => :string, :aliases => "-u"
|
|
34
|
+
method_option :template, :type => :string, :aliases => "-t"
|
|
33
35
|
method_option :concurrency, :type => :string, :aliases => "-c",
|
|
34
36
|
:banner => '"alpha=5,bar=3"'
|
|
35
37
|
|
|
@@ -39,6 +41,8 @@ class Foreman::CLI < Thor
|
|
|
39
41
|
formatter = case format
|
|
40
42
|
when "inittab" then Foreman::Export::Inittab
|
|
41
43
|
when "upstart" then Foreman::Export::Upstart
|
|
44
|
+
when "bluepill" then Foreman::Export::Bluepill
|
|
45
|
+
when "runit" then Foreman::Export::Runit
|
|
42
46
|
else error "Unknown export format: #{format}."
|
|
43
47
|
end
|
|
44
48
|
|
|
@@ -51,9 +55,8 @@ class Foreman::CLI < Thor
|
|
|
51
55
|
desc "check", "Validate your application's Procfile"
|
|
52
56
|
|
|
53
57
|
def check
|
|
54
|
-
processes
|
|
55
|
-
|
|
56
|
-
display "valid procfile detected (#{processes.join(', ')})"
|
|
58
|
+
error "no processes defined" unless engine.processes.length > 0
|
|
59
|
+
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
private ######################################################################
|
|
@@ -63,7 +66,7 @@ private ######################################################################
|
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
def engine
|
|
66
|
-
@engine ||= Foreman::Engine.new(procfile)
|
|
69
|
+
@engine ||= Foreman::Engine.new(procfile, options)
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def procfile
|
|
@@ -86,7 +89,7 @@ private ######################################################################
|
|
|
86
89
|
def options
|
|
87
90
|
original_options = super
|
|
88
91
|
return original_options unless File.exists?(".foreman")
|
|
89
|
-
defaults = YAML::load_file(".foreman")
|
|
92
|
+
defaults = YAML::load_file(".foreman") || {}
|
|
90
93
|
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
|
91
94
|
end
|
|
92
95
|
|
data/lib/foreman/engine.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
require "foreman"
|
|
2
2
|
require "foreman/process"
|
|
3
|
+
require "foreman/procfile"
|
|
3
4
|
require "foreman/utils"
|
|
4
5
|
require "pty"
|
|
5
6
|
require "tempfile"
|
|
7
|
+
require "timeout"
|
|
6
8
|
require "term/ansicolor"
|
|
7
9
|
require "fileutils"
|
|
8
10
|
|
|
@@ -10,84 +12,73 @@ class Foreman::Engine
|
|
|
10
12
|
|
|
11
13
|
attr_reader :procfile
|
|
12
14
|
attr_reader :directory
|
|
15
|
+
attr_reader :options
|
|
13
16
|
|
|
14
17
|
extend Term::ANSIColor
|
|
15
18
|
|
|
16
19
|
COLORS = [ cyan, yellow, green, magenta, red ]
|
|
17
20
|
|
|
18
|
-
def initialize(procfile)
|
|
19
|
-
@procfile =
|
|
21
|
+
def initialize(procfile, options={})
|
|
22
|
+
@procfile = Foreman::Procfile.new(procfile)
|
|
20
23
|
@directory = File.expand_path(File.dirname(procfile))
|
|
24
|
+
@options = options
|
|
25
|
+
@environment = read_environment_files(options[:env])
|
|
21
26
|
end
|
|
22
27
|
|
|
23
|
-
def
|
|
24
|
-
@
|
|
25
|
-
|
|
26
|
-
procfile.split("\n").inject({}) do |hash, line|
|
|
27
|
-
next if line.strip == ""
|
|
28
|
-
name, command = line.split(/ *: +/, 2)
|
|
29
|
-
unless command
|
|
30
|
-
warn_deprecated_procfile!
|
|
31
|
-
name, command = line.split(/ +/, 2)
|
|
32
|
-
end
|
|
33
|
-
process = Foreman::Process.new(name, command)
|
|
34
|
-
process.color = next_color
|
|
35
|
-
@order << process.name
|
|
36
|
-
hash.update(process.name => process)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def process_order
|
|
42
|
-
processes
|
|
43
|
-
@order.uniq
|
|
28
|
+
def self.load_env!(env_file)
|
|
29
|
+
@environment = read_environment_files(env_file)
|
|
30
|
+
apply_environment!
|
|
44
31
|
end
|
|
45
32
|
|
|
46
|
-
def
|
|
47
|
-
process_order.map do |name|
|
|
48
|
-
[name, processes[name]]
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def start(options={})
|
|
33
|
+
def start
|
|
53
34
|
proctitle "ruby: foreman master"
|
|
35
|
+
termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)"
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
processes.each do |process|
|
|
38
|
+
process.color = next_color
|
|
39
|
+
fork process
|
|
57
40
|
end
|
|
58
41
|
|
|
59
|
-
trap("TERM") { puts "SIGTERM received";
|
|
60
|
-
trap("INT") { puts "SIGINT received";
|
|
42
|
+
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
43
|
+
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
61
44
|
|
|
62
45
|
watch_for_termination
|
|
63
46
|
end
|
|
64
47
|
|
|
65
|
-
def execute(name
|
|
66
|
-
|
|
48
|
+
def execute(name)
|
|
49
|
+
error "no such process: #{name}" unless process = procfile[name]
|
|
50
|
+
process.color = next_color
|
|
51
|
+
fork process
|
|
67
52
|
|
|
68
|
-
trap("TERM") { puts "SIGTERM received";
|
|
69
|
-
trap("INT") { puts "SIGINT received";
|
|
53
|
+
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
54
|
+
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
70
55
|
|
|
71
56
|
watch_for_termination
|
|
72
57
|
end
|
|
73
58
|
|
|
59
|
+
def processes
|
|
60
|
+
procfile.processes
|
|
61
|
+
end
|
|
62
|
+
|
|
74
63
|
def port_for(process, num, base_port=nil)
|
|
75
64
|
base_port ||= 5000
|
|
76
|
-
offset =
|
|
65
|
+
offset = procfile.process_names.index(process.name) * 100
|
|
77
66
|
base_port.to_i + offset + num - 1
|
|
78
67
|
end
|
|
79
68
|
|
|
80
69
|
private ######################################################################
|
|
81
70
|
|
|
82
|
-
def fork(process
|
|
83
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
71
|
+
def fork(process)
|
|
72
|
+
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
|
84
73
|
|
|
85
74
|
1.upto(concurrency[process.name]) do |num|
|
|
86
|
-
fork_individual(process, num, port_for(process, num, options[:port]))
|
|
75
|
+
fork_individual(process, num, port_for(process, num, @options[:port]))
|
|
87
76
|
end
|
|
88
77
|
end
|
|
89
78
|
|
|
90
79
|
def fork_individual(process, num, port)
|
|
80
|
+
apply_environment!
|
|
81
|
+
|
|
91
82
|
ENV["PORT"] = port.to_s
|
|
92
83
|
ENV["PS"] = "#{process.name}.#{num}"
|
|
93
84
|
|
|
@@ -101,18 +92,18 @@ private ######################################################################
|
|
|
101
92
|
|
|
102
93
|
def run(process)
|
|
103
94
|
proctitle "ruby: foreman #{process.name}"
|
|
95
|
+
trap("SIGINT", "IGNORE")
|
|
104
96
|
|
|
105
97
|
begin
|
|
106
98
|
Dir.chdir directory do
|
|
107
|
-
command
|
|
108
|
-
|
|
109
|
-
PTY.spawn("#{process.command} 2>&1") do |stdin, stdout, pid|
|
|
99
|
+
PTY.spawn(process.command) do |stdin, stdout, pid|
|
|
100
|
+
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
|
110
101
|
until stdin.eof?
|
|
111
102
|
info stdin.gets, process
|
|
112
103
|
end
|
|
113
104
|
end
|
|
114
105
|
end
|
|
115
|
-
rescue PTY::ChildExited, Interrupt
|
|
106
|
+
rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
|
|
116
107
|
begin
|
|
117
108
|
info "process exiting", process
|
|
118
109
|
rescue Interrupt
|
|
@@ -120,14 +111,30 @@ private ######################################################################
|
|
|
120
111
|
end
|
|
121
112
|
end
|
|
122
113
|
|
|
123
|
-
def kill_all(signal="
|
|
124
|
-
info "terminating"
|
|
114
|
+
def kill_all(signal="SIGTERM")
|
|
125
115
|
running_processes.each do |pid, process|
|
|
126
|
-
|
|
127
|
-
Process.kill(signal, pid)
|
|
116
|
+
Process.kill(signal, pid) rescue Errno::ESRCH
|
|
128
117
|
end
|
|
129
118
|
end
|
|
130
119
|
|
|
120
|
+
def terminate_gracefully
|
|
121
|
+
info "sending SIGTERM to all processes"
|
|
122
|
+
kill_all "SIGTERM"
|
|
123
|
+
Timeout.timeout(3) { Process.waitall }
|
|
124
|
+
rescue Timeout::Error
|
|
125
|
+
info "sending SIGKILL to all processes"
|
|
126
|
+
kill_all "SIGKILL"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def watch_for_termination
|
|
130
|
+
pid, status = Process.wait2
|
|
131
|
+
process = running_processes.delete(pid)
|
|
132
|
+
info "process terminated", process
|
|
133
|
+
terminate_gracefully
|
|
134
|
+
kill_all
|
|
135
|
+
rescue Errno::ECHILD
|
|
136
|
+
end
|
|
137
|
+
|
|
131
138
|
def info(message, process=nil)
|
|
132
139
|
print process.color if process
|
|
133
140
|
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
|
@@ -136,9 +143,14 @@ private ######################################################################
|
|
|
136
143
|
puts
|
|
137
144
|
end
|
|
138
145
|
|
|
146
|
+
def error(message)
|
|
147
|
+
puts "ERROR: #{message}"
|
|
148
|
+
exit 1
|
|
149
|
+
end
|
|
150
|
+
|
|
139
151
|
def longest_process_name
|
|
140
152
|
@longest_process_name ||= begin
|
|
141
|
-
longest =
|
|
153
|
+
longest = procfile.process_names.map { |name| name.length }.sort.last
|
|
142
154
|
longest = 6 if longest < 6 # system
|
|
143
155
|
longest
|
|
144
156
|
end
|
|
@@ -149,27 +161,12 @@ private ######################################################################
|
|
|
149
161
|
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
|
150
162
|
end
|
|
151
163
|
|
|
152
|
-
def print_info
|
|
153
|
-
info "currently running processes:"
|
|
154
|
-
running_processes.each do |pid, process|
|
|
155
|
-
info "pid #{pid}", process
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
164
|
def proctitle(title)
|
|
160
165
|
$0 = title
|
|
161
166
|
end
|
|
162
167
|
|
|
163
|
-
def
|
|
164
|
-
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def watch_for_termination
|
|
168
|
-
pid, status = Process.wait2
|
|
169
|
-
process = running_processes.delete(pid)
|
|
170
|
-
info "process terminated", process
|
|
171
|
-
kill_all
|
|
172
|
-
Process.waitall
|
|
168
|
+
def termtitle(title)
|
|
169
|
+
printf("\033]0;#{title}\007")
|
|
173
170
|
end
|
|
174
171
|
|
|
175
172
|
def running_processes
|
|
@@ -182,12 +179,37 @@ private ######################################################################
|
|
|
182
179
|
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
|
183
180
|
end
|
|
184
181
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
module Env
|
|
183
|
+
attr_reader :environment
|
|
184
|
+
|
|
185
|
+
def read_environment_files(filenames)
|
|
186
|
+
environment = {}
|
|
187
|
+
|
|
188
|
+
(filenames || "").split(",").map(&:strip).each do |filename|
|
|
189
|
+
error "No such file: #{filename}" unless File.exists?(filename)
|
|
190
|
+
environment.merge!(read_environment(filename))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
environment.merge!(read_environment(".env")) unless filenames
|
|
194
|
+
environment
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def read_environment(filename)
|
|
198
|
+
return {} unless File.exists?(filename)
|
|
199
|
+
|
|
200
|
+
File.read(filename).split("\n").inject({}) do |hash, line|
|
|
201
|
+
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
|
|
202
|
+
hash[$1] = $2
|
|
203
|
+
end
|
|
204
|
+
hash
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def apply_environment!
|
|
209
|
+
@environment.each { |k,v| ENV[k] = v }
|
|
210
|
+
end
|
|
191
211
|
end
|
|
192
212
|
|
|
213
|
+
include Env
|
|
214
|
+
extend Env
|
|
193
215
|
end
|
data/lib/foreman/export/base.rb
CHANGED
|
@@ -23,8 +23,14 @@ private ######################################################################
|
|
|
23
23
|
puts "[foreman export] %s" % message
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def export_template(
|
|
27
|
-
File.
|
|
26
|
+
def export_template(exporter, file, template_root)
|
|
27
|
+
if template_root && File.exist?(file_path = File.join(template_root, file))
|
|
28
|
+
File.read(file_path)
|
|
29
|
+
elsif File.exist?(file_path = File.join("~/.foreman/templates", file))
|
|
30
|
+
File.read(file_path)
|
|
31
|
+
else
|
|
32
|
+
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
|
|
33
|
+
end
|
|
28
34
|
end
|
|
29
35
|
|
|
30
36
|
def write_file(filename, contents)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "foreman/export"
|
|
3
|
+
|
|
4
|
+
class Foreman::Export::Bluepill < Foreman::Export::Base
|
|
5
|
+
|
|
6
|
+
def export(location, options={})
|
|
7
|
+
error("Must specify a location") unless location
|
|
8
|
+
|
|
9
|
+
FileUtils.mkdir_p location
|
|
10
|
+
|
|
11
|
+
app = options[:app] || File.basename(engine.directory)
|
|
12
|
+
user = options[:user] || app
|
|
13
|
+
log_root = options[:log] || "/var/log/#{app}"
|
|
14
|
+
template_root = options[:template]
|
|
15
|
+
|
|
16
|
+
Dir["#{location}/#{app}.pill"].each do |file|
|
|
17
|
+
say "cleaning up: #{file}"
|
|
18
|
+
FileUtils.rm(file)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
22
|
+
|
|
23
|
+
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
|
24
|
+
master_config = ERB.new(master_template).result(binding)
|
|
25
|
+
write_file "#{location}/#{app}.pill", master_config
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require "foreman/export
|
|
1
|
+
require "foreman/export"
|
|
2
2
|
|
|
3
3
|
class Foreman::Export::Inittab < Foreman::Export::Base
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
|
|
|
12
12
|
inittab = []
|
|
13
13
|
inittab << "# ----- foreman #{app} processes -----"
|
|
14
14
|
|
|
15
|
-
engine.processes.
|
|
15
|
+
engine.processes.inject(1) do |index, process|
|
|
16
16
|
1.upto(concurrency[process.name]) do |num|
|
|
17
17
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
|
18
18
|
port = engine.port_for(process, num, options[:port])
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "foreman/export"
|
|
3
|
+
|
|
4
|
+
class Foreman::Export::Runit < Foreman::Export::Base
|
|
5
|
+
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
|
6
|
+
|
|
7
|
+
def export(location, options={})
|
|
8
|
+
error("Must specify a location") unless location
|
|
9
|
+
|
|
10
|
+
app = options[:app] || File.basename(engine.directory)
|
|
11
|
+
user = options[:user] || app
|
|
12
|
+
log_root = options[:log] || "/var/log/#{app}"
|
|
13
|
+
template_root = options[:template]
|
|
14
|
+
|
|
15
|
+
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
16
|
+
|
|
17
|
+
run_template = export_template('runit', 'run.erb', template_root)
|
|
18
|
+
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
|
19
|
+
|
|
20
|
+
engine.processes.each do |process|
|
|
21
|
+
1.upto(concurrency[process.name]) do |num|
|
|
22
|
+
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
|
23
|
+
process_env_directory = "#{process_directory}/env"
|
|
24
|
+
process_log_directory = "#{process_directory}/log"
|
|
25
|
+
|
|
26
|
+
create_directory process_directory
|
|
27
|
+
create_directory process_env_directory
|
|
28
|
+
create_directory process_log_directory
|
|
29
|
+
|
|
30
|
+
run = ERB.new(run_template).result(binding)
|
|
31
|
+
write_file "#{process_directory}/run", run
|
|
32
|
+
|
|
33
|
+
port = engine.port_for(process, num, options[:port])
|
|
34
|
+
environment_variables = {'PORT' => port}.
|
|
35
|
+
merge(engine.environment).
|
|
36
|
+
merge(inline_variables(process.command))
|
|
37
|
+
|
|
38
|
+
environment_variables.each_pair do |var, env|
|
|
39
|
+
write_file "#{process_env_directory}/#{var.upcase}", env
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
log_run = ERB.new(log_run_template).result(binding)
|
|
43
|
+
write_file "#{process_log_directory}/run", log_run
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
def create_directory(location)
|
|
52
|
+
say "creating: #{location}"
|
|
53
|
+
FileUtils.mkdir(location)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def inline_variables(command)
|
|
57
|
+
variable_name_regex =
|
|
58
|
+
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "erb"
|
|
2
|
-
require "foreman/export
|
|
2
|
+
require "foreman/export"
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Upstart < Foreman::Export::Base
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|
|
11
11
|
app = options[:app] || File.basename(engine.directory)
|
|
12
12
|
user = options[:user] || app
|
|
13
13
|
log_root = options[:log] || "/var/log/#{app}"
|
|
14
|
+
template_root = options[:template]
|
|
14
15
|
|
|
15
16
|
Dir["#{location}/#{app}*.conf"].each do |file|
|
|
16
17
|
say "cleaning up: #{file}"
|
|
@@ -19,14 +20,15 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|
|
19
20
|
|
|
20
21
|
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
21
22
|
|
|
22
|
-
master_template = export_template("upstart
|
|
23
|
+
master_template = export_template("upstart", "master.conf.erb", template_root)
|
|
23
24
|
master_config = ERB.new(master_template).result(binding)
|
|
24
25
|
write_file "#{location}/#{app}.conf", master_config
|
|
25
26
|
|
|
26
|
-
process_template = export_template("upstart
|
|
27
|
+
process_template = export_template("upstart", "process.conf.erb", template_root)
|
|
27
28
|
|
|
28
|
-
engine.processes.
|
|
29
|
-
|
|
29
|
+
engine.processes.each do |process|
|
|
30
|
+
next if (conc = concurrency[process.name]) < 1
|
|
31
|
+
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
|
30
32
|
process_master_config = ERB.new(process_master_template).result(binding)
|
|
31
33
|
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
|
32
34
|
|
data/lib/foreman/export.rb
CHANGED