foreman 0.46.0-java → 0.48.0.pre1-java

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.
Files changed (67) hide show
  1. data/README.md +6 -0
  2. data/bin/foreman-runner +3 -7
  3. data/bin/taskman +8 -0
  4. data/data/example/Procfile +4 -3
  5. data/data/example/spawnee +14 -0
  6. data/data/example/spawner +7 -0
  7. data/data/export/bluepill/master.pill.erb +11 -10
  8. data/data/export/launchd/launchd.plist.erb +22 -0
  9. data/data/export/runit/log/run.erb +7 -0
  10. data/data/export/runit/run.erb +2 -2
  11. data/data/export/supervisord/app.conf.erb +12 -12
  12. data/data/export/upstart/master.conf.erb +2 -2
  13. data/data/export/upstart/process.conf.erb +3 -3
  14. data/lib/foreman/cli.rb +51 -22
  15. data/lib/foreman/engine.rb +209 -148
  16. data/lib/foreman/engine/cli.rb +98 -0
  17. data/lib/foreman/env.rb +27 -0
  18. data/lib/foreman/export.rb +1 -1
  19. data/lib/foreman/export/base.rb +58 -20
  20. data/lib/foreman/export/bluepill.rb +3 -17
  21. data/lib/foreman/export/inittab.rb +8 -11
  22. data/lib/foreman/export/launchd.rb +15 -0
  23. data/lib/foreman/export/runit.rb +14 -39
  24. data/lib/foreman/export/supervisord.rb +3 -13
  25. data/lib/foreman/export/upstart.rb +9 -27
  26. data/lib/foreman/process.rb +56 -67
  27. data/lib/foreman/procfile.rb +59 -25
  28. data/lib/foreman/version.rb +1 -1
  29. data/man/foreman.1 +5 -1
  30. data/spec/foreman/cli_spec.rb +38 -152
  31. data/spec/foreman/engine_spec.rb +47 -74
  32. data/spec/foreman/export/base_spec.rb +4 -7
  33. data/spec/foreman/export/bluepill_spec.rb +7 -6
  34. data/spec/foreman/export/inittab_spec.rb +7 -7
  35. data/spec/foreman/export/launchd_spec.rb +21 -0
  36. data/spec/foreman/export/runit_spec.rb +12 -17
  37. data/spec/foreman/export/supervisord_spec.rb +7 -56
  38. data/spec/foreman/export/upstart_spec.rb +22 -21
  39. data/spec/foreman/process_spec.rb +27 -110
  40. data/spec/foreman/procfile_spec.rb +26 -16
  41. data/spec/resources/Procfile +4 -0
  42. data/spec/resources/bin/echo +2 -0
  43. data/spec/resources/bin/env +2 -0
  44. data/spec/resources/bin/test +2 -0
  45. data/spec/resources/export/bluepill/app-concurrency.pill +6 -4
  46. data/spec/resources/export/bluepill/app.pill +6 -4
  47. data/spec/resources/export/launchd/launchd-a.default +22 -0
  48. data/spec/resources/export/launchd/launchd-b.default +22 -0
  49. data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
  50. data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
  51. data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
  52. data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
  53. data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
  54. data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
  55. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  56. data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
  57. data/spec/spec_helper.rb +58 -6
  58. metadata +32 -24
  59. data/data/export/runit/log_run.erb +0 -7
  60. data/lib/foreman/color.rb +0 -40
  61. data/lib/foreman/procfile_entry.rb +0 -26
  62. data/lib/foreman/utils.rb +0 -18
  63. data/spec/foreman/color_spec.rb +0 -31
  64. data/spec/foreman/procfile_entry_spec.rb +0 -13
  65. data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
  66. data/spec/resources/export/supervisord/app-env.conf +0 -21
  67. data/spec/resources/export/supervisord/app.conf +0 -24
@@ -0,0 +1,98 @@
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)
28
+ io.extend(self)
29
+ end
30
+
31
+ def color?
32
+ return false unless self.respond_to?(:isatty)
33
+ self.isatty && ENV["TERM"]
34
+ end
35
+
36
+ def color(name)
37
+ return "" unless color?
38
+ return "" unless ansi = ANSI[name.to_sym]
39
+ "\e[#{ansi}m"
40
+ end
41
+
42
+ end
43
+
44
+ FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
45
+ intense_green intense_magenta intense_red, intense_blue )
46
+
47
+ def startup
48
+ @colors = map_colors
49
+ proctitle "foreman: master"
50
+ end
51
+
52
+ def output(name, data)
53
+ data.to_s.chomp.split("\n").each do |message|
54
+ Color.enable($stdout) unless $stdout.respond_to?(:color?)
55
+ output = ""
56
+ output += $stdout.color(@colors[name.split(".").first].to_sym)
57
+ output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
58
+ output += $stdout.color(:reset)
59
+ output += message
60
+ $stdout.puts output
61
+ end
62
+ end
63
+
64
+ def shutdown
65
+ end
66
+
67
+ private
68
+
69
+ def name_padding
70
+ @name_padding ||= begin
71
+ index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
72
+ name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
73
+ [ 6, name_padding ].max
74
+ end
75
+ end
76
+
77
+ def pad_process_name(name)
78
+ name.ljust(name_padding, " ")
79
+ end
80
+
81
+ def map_colors
82
+ colors = Hash.new("white")
83
+ @names.values.each_with_index do |name, index|
84
+ colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
85
+ end
86
+ colors["system"] = "intense_white"
87
+ colors
88
+ end
89
+
90
+ def proctitle(title)
91
+ $0 = title
92
+ end
93
+
94
+ def termtitle(title)
95
+ printf("\033]0;#{title}\007") unless Foreman.windows?
96
+ end
97
+
98
+ end
@@ -0,0 +1,27 @@
1
+ require "foreman"
2
+
3
+ class Foreman::Env
4
+
5
+ attr_reader :entries
6
+
7
+ def initialize(filename)
8
+ @entries = File.read(filename).split("\n").inject({}) do |ax, line|
9
+ if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
10
+ key = $1
11
+ case val = $2
12
+ when /\A'(.*)'\z/ then ax[key] = $1
13
+ when /\A"(.*)"\z/ then ax[key] = $1.gsub(/\\(.)/, '\1')
14
+ else ax[key] = val
15
+ end
16
+ end
17
+ ax
18
+ end
19
+ end
20
+
21
+ def entries
22
+ @entries.each do |key, value|
23
+ yield key, value
24
+ end
25
+ end
26
+
27
+ end
@@ -24,11 +24,11 @@ module Foreman::Export
24
24
 
25
25
  end
26
26
 
27
-
28
27
  require "foreman/export/base"
29
28
  require "foreman/export/inittab"
30
29
  require "foreman/export/upstart"
31
30
  require "foreman/export/bluepill"
32
31
  require "foreman/export/runit"
33
32
  require "foreman/export/supervisord"
33
+ require "foreman/export/launchd"
34
34
 
@@ -1,23 +1,37 @@
1
1
  require "foreman/export"
2
- require "foreman/utils"
2
+ require "shellwords"
3
3
 
4
4
  class Foreman::Export::Base
5
5
 
6
- attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
6
+ attr_reader :location
7
+ attr_reader :engine
8
+ attr_reader :options
9
+ attr_reader :formation
7
10
 
8
11
  def initialize(location, engine, options={})
9
- @location = location
10
- @engine = engine
11
- @app = options[:app]
12
- @log = options[:log]
13
- @port = options[:port]
14
- @user = options[:user]
15
- @template = options[:template]
16
- @concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
12
+ @location = location
13
+ @engine = engine
14
+ @options = options.dup
15
+ @formation = engine.formation
17
16
  end
18
17
 
19
18
  def export
20
- raise "export method must be overridden"
19
+ error("Must specify a location") unless location
20
+ FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
21
+ FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
22
+ FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
23
+ end
24
+
25
+ def app
26
+ options[:app] || "app"
27
+ end
28
+
29
+ def log
30
+ options[:log] || "/var/log/#{app}"
31
+ end
32
+
33
+ def user
34
+ options[:user] || app
21
35
  end
22
36
 
23
37
  private ######################################################################
@@ -29,21 +43,45 @@ private ######################################################################
29
43
  def say(message)
30
44
  puts "[foreman export] %s" % message
31
45
  end
46
+
47
+ def clean(filename)
48
+ return unless File.exists?(filename)
49
+ say "cleaning up: #{filename}"
50
+ FileUtils.rm(filename)
51
+ end
32
52
 
33
- def export_template(exporter, file, template_root)
34
- if template_root && File.exist?(file_path = File.join(template_root, file))
35
- File.read(file_path)
36
- elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
37
- File.read(file_path)
38
- else
39
- File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
40
- end
53
+ def shell_quote(value)
54
+ '"' + Shellwords.escape(value) + '"'
55
+ end
56
+
57
+ def export_template(name)
58
+ name_without_first = name.split("/")[1..-1].join("/")
59
+ matchers = []
60
+ matchers << File.join(options[:template], name_without_first) if options[:template]
61
+ matchers << File.expand_path("~/.foreman/templates/#{name}")
62
+ matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
63
+ File.read(matchers.detect { |m| File.exists?(m) })
64
+ end
65
+
66
+ def write_template(name, target, binding)
67
+ compiled = ERB.new(export_template(name)).result(binding)
68
+ write_file target, compiled
69
+ end
70
+
71
+ def chmod(mode, file)
72
+ say "setting #{file} to mode #{mode}"
73
+ FileUtils.chmod mode, File.join(location, file)
74
+ end
75
+
76
+ def create_directory(dir)
77
+ say "creating: #{dir}"
78
+ FileUtils.mkdir_p(File.join(location, dir))
41
79
  end
42
80
 
43
81
  def write_file(filename, contents)
44
82
  say "writing: #{filename}"
45
83
 
46
- File.open(filename, "w") do |file|
84
+ File.open(File.join(location, filename), "w") do |file|
47
85
  file.puts contents
48
86
  end
49
87
  end
@@ -4,23 +4,9 @@ require "foreman/export"
4
4
  class Foreman::Export::Bluepill < Foreman::Export::Base
5
5
 
6
6
  def export
7
- error("Must specify a location") unless location
8
-
9
- FileUtils.mkdir_p location
10
-
11
- app = self.app || File.basename(engine.directory)
12
- user = self.user || app
13
- log_root = self.log || "/var/log/#{app}"
14
- template_root = self.template
15
-
16
- Dir["#{location}/#{app}.pill"].each do |file|
17
- say "cleaning up: #{file}"
18
- FileUtils.rm(file)
19
- end
20
-
21
- master_template = export_template("bluepill", "master.pill.erb", template_root)
22
- master_config = ERB.new(master_template).result(binding)
23
- write_file "#{location}/#{app}.pill", master_config
7
+ super
8
+ clean "#{location}/#{app}.pill"
9
+ write_template "bluepill/master.pill.erb", "#{app}.pill", binding
24
10
  end
25
11
 
26
12
  end
@@ -3,21 +3,19 @@ require "foreman/export"
3
3
  class Foreman::Export::Inittab < Foreman::Export::Base
4
4
 
5
5
  def export
6
- app = self.app || File.basename(engine.directory)
7
- user = self.user || app
8
- log_root = self.log || "/var/log/#{app}"
6
+ error("Must specify a location") unless location
9
7
 
10
8
  inittab = []
11
9
  inittab << "# ----- foreman #{app} processes -----"
12
10
 
13
- engine.procfile.entries.inject(1) do |index, process|
14
- 1.upto(self.concurrency[process.name]) do |num|
11
+ index = 1
12
+ engine.each_process do |name, process|
13
+ 1.upto(engine.formation[name]) do |num|
15
14
  id = app.slice(0, 2).upcase + sprintf("%02d", index)
16
- port = engine.port_for(process, num, self.port)
17
- inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
15
+ port = engine.port_for(process, num)
16
+ inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
18
17
  index += 1
19
18
  end
20
- index
21
19
  end
22
20
 
23
21
  inittab << "# ----- end foreman #{app} processes -----"
@@ -27,9 +25,8 @@ class Foreman::Export::Inittab < Foreman::Export::Base
27
25
  if location == "-"
28
26
  puts inittab
29
27
  else
30
- FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
31
- FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
32
- write_file(location, inittab)
28
+ say "writing: #{location}"
29
+ File.open(location, "w") { |file| file.puts inittab }
33
30
  end
34
31
  end
35
32
 
@@ -0,0 +1,15 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Launchd < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+ engine.each_process do |name, process|
9
+ 1.upto(engine.formation[name]) do |num|
10
+ write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
11
+ end
12
+ end
13
+ end
14
+
15
+ end
@@ -2,58 +2,33 @@ require "erb"
2
2
  require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Runit < Foreman::Export::Base
5
+
5
6
  ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
6
7
 
7
8
  def export
8
- error("Must specify a location") unless location
9
-
10
- app = self.app || File.basename(engine.directory)
11
- user = self.user || app
12
- log_root = self.log || "/var/log/#{app}"
13
- template_root = self.template
9
+ super
14
10
 
15
- run_template = export_template('runit', 'run.erb', template_root)
16
- log_run_template = export_template('runit', 'log_run.erb', template_root)
17
-
18
- engine.procfile.entries.each do |process|
19
- 1.upto(self.concurrency[process.name]) do |num|
20
- process_directory = "#{location}/#{app}-#{process.name}-#{num}"
21
- process_env_directory = "#{process_directory}/env"
22
- process_log_directory = "#{process_directory}/log"
11
+ engine.each_process do |name, process|
12
+ 1.upto(engine.formation[name]) do |num|
13
+ process_directory = "#{app}-#{name}-#{num}"
23
14
 
24
15
  create_directory process_directory
25
- create_directory process_env_directory
26
- create_directory process_log_directory
27
-
28
- run = ERB.new(run_template).result(binding)
29
- write_file "#{process_directory}/run", run
30
- FileUtils.chmod 0755, "#{process_directory}/run"
16
+ create_directory "#{process_directory}/env"
17
+ create_directory "#{process_directory}/log"
31
18
 
32
- port = engine.port_for(process, num, self.port)
33
- environment_variables = {'PORT' => port}.
34
- merge(engine.environment).
35
- merge(inline_variables(process.command))
19
+ write_template "runit/run.erb", "#{process_directory}/run", binding
20
+ chmod 0755, "#{process_directory}/run"
36
21
 
37
- environment_variables.each_pair do |var, env|
38
- write_file "#{process_env_directory}/#{var.upcase}", env
22
+ port = engine.port_for(process, num)
23
+ engine.env.merge("PORT" => port.to_s).each do |key, value|
24
+ write_file "#{process_directory}/env/#{key}", value
39
25
  end
40
26
 
41
- log_run = ERB.new(log_run_template).result(binding)
42
- write_file "#{process_log_directory}/run", log_run
43
- FileUtils.chmod 0755, "#{process_log_directory}/run"
27
+ write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
28
+ chmod 0755, "#{process_directory}/log/run"
44
29
  end
45
30
  end
46
31
 
47
32
  end
48
33
 
49
- private
50
- def create_directory(location)
51
- say "creating: #{location}"
52
- FileUtils.mkdir_p(location)
53
- end
54
-
55
- def inline_variables(command)
56
- variable_name_regex =
57
- Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
58
- end
59
34
  end
@@ -4,23 +4,13 @@ require "foreman/export"
4
4
  class Foreman::Export::Supervisord < Foreman::Export::Base
5
5
 
6
6
  def export
7
- error("Must specify a location") unless location
8
-
9
- FileUtils.mkdir_p location
10
-
11
- app = self.app || File.basename(engine.directory)
12
- user = self.user || app
13
- log_root = self.log || "/var/log/#{app}"
14
- template_root = self.template
7
+ super
15
8
 
16
9
  Dir["#{location}/#{app}*.conf"].each do |file|
17
- say "cleaning up: #{file}"
18
- FileUtils.rm(file)
10
+ clean file
19
11
  end
20
12
 
21
- app_template = export_template("supervisord", "app.conf.erb", template_root)
22
- app_config = ERB.new(app_template, 0, '<').result(binding)
23
- write_file "#{location}/#{app}.conf", app_config
13
+ write_template "supervisord/app.conf.erb", "#{app}.conf", binding
24
14
  end
25
15
 
26
16
  end
@@ -4,40 +4,22 @@ require "foreman/export"
4
4
  class Foreman::Export::Upstart < Foreman::Export::Base
5
5
 
6
6
  def export
7
- error("Must specify a location") unless location
8
-
9
- FileUtils.mkdir_p location
10
-
11
- app = self.app || File.basename(engine.directory)
12
- user = self.user || app
13
- log_root = self.log || "/var/log/#{app}"
14
- template_root = self.template
7
+ super
15
8
 
16
9
  Dir["#{location}/#{app}*.conf"].each do |file|
17
- say "cleaning up: #{file}"
18
- FileUtils.rm(file)
10
+ clean file
19
11
  end
20
12
 
21
- master_template = export_template("upstart", "master.conf.erb", template_root)
22
- master_config = ERB.new(master_template).result(binding)
23
- write_file "#{location}/#{app}.conf", master_config
13
+ write_template "upstart/master.conf.erb", "#{app}.conf", binding
24
14
 
25
- process_template = export_template("upstart", "process.conf.erb", template_root)
15
+ engine.each_process do |name, process|
16
+ next if engine.formation[name] < 1
17
+ write_template "upstart/process_master.conf.erb", "#{app}-#{name}.conf", binding
26
18
 
27
- engine.procfile.entries.each do |process|
28
- next if (conc = self.concurrency[process.name]) < 1
29
- process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
30
- process_master_config = ERB.new(process_master_template).result(binding)
31
- write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
32
-
33
- 1.upto(self.concurrency[process.name]) do |num|
34
- port = engine.port_for(process, num, self.port)
35
- process_config = ERB.new(process_template).result(binding)
36
- write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
19
+ 1.upto(engine.formation[name]) do |num|
20
+ port = engine.port_for(process, num)
21
+ write_template "upstart/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
37
22
  end
38
23
  end
39
-
40
- FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
41
- FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
42
24
  end
43
25
  end