ypadlyak-foreman 0.81.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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +53 -0
  3. data/bin/foreman +7 -0
  4. data/bin/foreman-runner +41 -0
  5. data/data/example/Procfile +4 -0
  6. data/data/example/Procfile.without_colon +2 -0
  7. data/data/example/error +7 -0
  8. data/data/example/log/neverdie.log +4 -0
  9. data/data/example/spawnee +14 -0
  10. data/data/example/spawner +7 -0
  11. data/data/example/ticker +14 -0
  12. data/data/example/utf8 +11 -0
  13. data/data/export/bluepill/master.pill.erb +28 -0
  14. data/data/export/daemon/master.conf.erb +14 -0
  15. data/data/export/daemon/process.conf.erb +8 -0
  16. data/data/export/daemon/process_master.conf.erb +2 -0
  17. data/data/export/launchd/launchd.plist.erb +33 -0
  18. data/data/export/runit/log/run.erb +7 -0
  19. data/data/export/runit/run.erb +4 -0
  20. data/data/export/supervisord/app.conf.erb +32 -0
  21. data/data/export/systemd/master.target.erb +5 -0
  22. data/data/export/systemd/process.service.erb +15 -0
  23. data/data/export/systemd/process_master.target.erb +3 -0
  24. data/data/export/upstart/master.conf.erb +2 -0
  25. data/data/export/upstart/process.conf.erb +18 -0
  26. data/data/export/upstart/process_master.conf.erb +2 -0
  27. data/lib/foreman/cli.rb +160 -0
  28. data/lib/foreman/distribution.rb +9 -0
  29. data/lib/foreman/engine/cli.rb +104 -0
  30. data/lib/foreman/engine.rb +486 -0
  31. data/lib/foreman/env.rb +29 -0
  32. data/lib/foreman/export/base.rb +156 -0
  33. data/lib/foreman/export/bluepill.rb +12 -0
  34. data/lib/foreman/export/daemon.rb +28 -0
  35. data/lib/foreman/export/inittab.rb +42 -0
  36. data/lib/foreman/export/launchd.rb +22 -0
  37. data/lib/foreman/export/runit.rb +34 -0
  38. data/lib/foreman/export/supervisord.rb +16 -0
  39. data/lib/foreman/export/systemd.rb +32 -0
  40. data/lib/foreman/export/upstart.rb +43 -0
  41. data/lib/foreman/export.rb +36 -0
  42. data/lib/foreman/helpers.rb +45 -0
  43. data/lib/foreman/process.rb +80 -0
  44. data/lib/foreman/procfile.rb +94 -0
  45. data/lib/foreman/version.rb +5 -0
  46. data/lib/foreman.rb +17 -0
  47. data/man/foreman.1 +284 -0
  48. data/spec/foreman/cli_spec.rb +111 -0
  49. data/spec/foreman/engine_spec.rb +114 -0
  50. data/spec/foreman/export/base_spec.rb +19 -0
  51. data/spec/foreman/export/bluepill_spec.rb +37 -0
  52. data/spec/foreman/export/daemon_spec.rb +97 -0
  53. data/spec/foreman/export/inittab_spec.rb +40 -0
  54. data/spec/foreman/export/launchd_spec.rb +31 -0
  55. data/spec/foreman/export/runit_spec.rb +36 -0
  56. data/spec/foreman/export/supervisord_spec.rb +38 -0
  57. data/spec/foreman/export/systemd_spec.rb +91 -0
  58. data/spec/foreman/export/upstart_spec.rb +118 -0
  59. data/spec/foreman/export_spec.rb +24 -0
  60. data/spec/foreman/helpers_spec.rb +26 -0
  61. data/spec/foreman/process_spec.rb +71 -0
  62. data/spec/foreman/procfile_spec.rb +50 -0
  63. data/spec/foreman_spec.rb +16 -0
  64. data/spec/helper_spec.rb +19 -0
  65. data/spec/resources/Procfile +5 -0
  66. data/spec/resources/Procfile.bad +2 -0
  67. data/spec/resources/bin/echo +2 -0
  68. data/spec/resources/bin/env +2 -0
  69. data/spec/resources/bin/test +2 -0
  70. data/spec/resources/bin/utf8 +2 -0
  71. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  72. data/spec/resources/export/bluepill/app.pill +81 -0
  73. data/spec/resources/export/daemon/app-alpha-1.conf +7 -0
  74. data/spec/resources/export/daemon/app-alpha-2.conf +7 -0
  75. data/spec/resources/export/daemon/app-alpha.conf +2 -0
  76. data/spec/resources/export/daemon/app-bravo-1.conf +7 -0
  77. data/spec/resources/export/daemon/app-bravo.conf +2 -0
  78. data/spec/resources/export/daemon/app.conf +14 -0
  79. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  80. data/spec/resources/export/inittab/inittab.default +6 -0
  81. data/spec/resources/export/launchd/launchd-a.default +29 -0
  82. data/spec/resources/export/launchd/launchd-b.default +29 -0
  83. data/spec/resources/export/launchd/launchd-c.default +30 -0
  84. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  85. data/spec/resources/export/runit/app-alpha-1/run +4 -0
  86. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  87. data/spec/resources/export/runit/app-alpha-2/run +4 -0
  88. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  89. data/spec/resources/export/runit/app-bravo-1/run +4 -0
  90. data/spec/resources/export/supervisord/app-alpha-1.conf +46 -0
  91. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  92. data/spec/resources/export/systemd/concurrency/app-alpha-1.service +14 -0
  93. data/spec/resources/export/systemd/concurrency/app-alpha-2.service +14 -0
  94. data/spec/resources/export/systemd/concurrency/app-alpha.target +3 -0
  95. data/spec/resources/export/systemd/concurrency/app.target +5 -0
  96. data/spec/resources/export/systemd/standard/app-alpha-1.service +14 -0
  97. data/spec/resources/export/systemd/standard/app-alpha.target +3 -0
  98. data/spec/resources/export/systemd/standard/app-bravo-1.service +14 -0
  99. data/spec/resources/export/systemd/standard/app-bravo.target +3 -0
  100. data/spec/resources/export/systemd/standard/app.target +5 -0
  101. data/spec/resources/export/upstart/app-alpha-1.conf +11 -0
  102. data/spec/resources/export/upstart/app-alpha-2.conf +11 -0
  103. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  104. data/spec/resources/export/upstart/app-bravo-1.conf +11 -0
  105. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  106. data/spec/resources/export/upstart/app.conf +2 -0
  107. data/spec/spec_helper.rb +180 -0
  108. metadata +164 -0
@@ -0,0 +1,42 @@
1
+ require "foreman/export"
2
+
3
+ class Foreman::Export::Inittab < Foreman::Export::Base
4
+
5
+ def export
6
+ error("Must specify a location") unless location
7
+
8
+ inittab = []
9
+ inittab << "# ----- foreman #{app} processes -----"
10
+
11
+ index = 1
12
+ engine.each_process do |name, process|
13
+ 1.upto(engine.formation[name]) do |num|
14
+ id = app.slice(0, 2).upcase + sprintf("%02d", index)
15
+ port = engine.port_for(process, num)
16
+
17
+ commands = []
18
+ commands << "cd #{engine.root}"
19
+ commands << "export PORT=#{port}"
20
+ engine.env.each_pair do |var, env|
21
+ commands << "export #{var.upcase}=#{shell_quote(env)}"
22
+ end
23
+ commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
24
+
25
+ inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
26
+ index += 1
27
+ end
28
+ end
29
+
30
+ inittab << "# ----- end foreman #{app} processes -----"
31
+
32
+ inittab = inittab.join("\n") + "\n"
33
+
34
+ if location == "-"
35
+ puts inittab
36
+ else
37
+ say "writing: #{location}"
38
+ File.open(location, "w") { |file| file.puts inittab }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,22 @@
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
+ port = engine.port_for(process, num)
11
+ command_args = process.command.split(/\s+/).map{|arg|
12
+ case arg
13
+ when "$PORT" then port
14
+ else arg
15
+ end
16
+ }
17
+ write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,34 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Runit < Foreman::Export::Base
5
+
6
+ ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
7
+
8
+ def export
9
+ super
10
+
11
+ engine.each_process do |name, process|
12
+ 1.upto(engine.formation[name]) do |num|
13
+ process_directory = "#{app}-#{name}-#{num}"
14
+
15
+ create_directory process_directory
16
+ create_directory "#{process_directory}/env"
17
+ create_directory "#{process_directory}/log"
18
+
19
+ write_template "runit/run.erb", "#{process_directory}/run", binding
20
+ chmod 0755, "#{process_directory}/run"
21
+
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
25
+ end
26
+
27
+ write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
28
+ chmod 0755, "#{process_directory}/log/run"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,16 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Supervisord < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}.conf"].each do |file|
10
+ clean file
11
+ end
12
+
13
+ write_template "supervisord/app.conf.erb", "#{app}.conf", binding
14
+ end
15
+
16
+ end
@@ -0,0 +1,32 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Systemd < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}*.target"].concat(Dir["#{location}/#{app}*.service"]).each do |file|
10
+ clean file
11
+ end
12
+
13
+ process_master_names = []
14
+
15
+ engine.each_process do |name, process|
16
+ next if engine.formation[name] < 1
17
+
18
+ process_names = []
19
+
20
+ 1.upto(engine.formation[name]) do |num|
21
+ port = engine.port_for(process, num)
22
+ write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
23
+ process_names << "#{app}-#{name}-#{num}.service"
24
+ end
25
+
26
+ write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
27
+ process_master_names << "#{app}-#{name}.target"
28
+ end
29
+
30
+ write_template "systemd/master.target.erb", "#{app}.target", binding
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Upstart < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ master_file = "#{app}.conf"
10
+
11
+ clean File.join(location, master_file)
12
+ write_template master_template, master_file, binding
13
+
14
+ engine.each_process do |name, process|
15
+ process_master_file = "#{app}-#{name}.conf"
16
+ clean File.join(location, process_master_file)
17
+
18
+ next if engine.formation[name] < 1
19
+ write_template process_master_template, process_master_file, binding
20
+
21
+ 1.upto(engine.formation[name]) do |num|
22
+ port = engine.port_for(process, num)
23
+ process_file = "#{app}-#{name}-#{num}.conf"
24
+ clean File.join(location, process_file)
25
+ write_template process_template, process_file, binding
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def master_template
33
+ "upstart/master.conf.erb"
34
+ end
35
+
36
+ def process_master_template
37
+ "upstart/process_master.conf.erb"
38
+ end
39
+
40
+ def process_template
41
+ "upstart/process.conf.erb"
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require "foreman"
2
+ require "foreman/helpers"
3
+ require "pathname"
4
+
5
+ module Foreman::Export
6
+ extend Foreman::Helpers
7
+
8
+ class Exception < ::Exception; end
9
+
10
+ def self.formatter(format)
11
+ begin
12
+ require "foreman/export/#{ format.tr('-', '_') }"
13
+ classy_format = classify(format)
14
+ formatter = constantize("Foreman::Export::#{ classy_format }")
15
+ rescue NameError => ex
16
+ error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
17
+ rescue LoadError => ex
18
+ error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
19
+ end
20
+ end
21
+
22
+ def self.error(message)
23
+ raise Foreman::Export::Exception.new(message)
24
+ end
25
+
26
+ end
27
+
28
+ require "foreman/export/base"
29
+ require "foreman/export/inittab"
30
+ require "foreman/export/upstart"
31
+ require "foreman/export/daemon"
32
+ require "foreman/export/bluepill"
33
+ require "foreman/export/runit"
34
+ require "foreman/export/supervisord"
35
+ require "foreman/export/launchd"
36
+ require "foreman/export/systemd"
@@ -0,0 +1,45 @@
1
+ module Foreman::Helpers
2
+ # Copied whole sale from, https://github.com/defunkt/resque/
3
+
4
+ # Given a word with dashes, returns a camel cased version of it.
5
+ #
6
+ # classify('job-name') # => 'JobName'
7
+ def classify(dashed_word)
8
+ dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
9
+ end # Tries to find a constant with the name specified in the argument string:
10
+
11
+ #
12
+ # constantize("Module") # => Module
13
+ # constantize("Test::Unit") # => Test::Unit
14
+ #
15
+ # The name is assumed to be the one of a top-level constant, no matter
16
+ # whether it starts with "::" or not. No lexical context is taken into
17
+ # account:
18
+ #
19
+ # C = 'outside'
20
+ # module M
21
+ # C = 'inside'
22
+ # C # => 'inside'
23
+ # constantize("C") # => 'outside', same as ::C
24
+ # end
25
+ #
26
+ # NameError is raised when the constant is unknown.
27
+ def constantize(camel_cased_word)
28
+ camel_cased_word = camel_cased_word.to_s
29
+
30
+ names = camel_cased_word.split('::')
31
+ names.shift if names.empty? || names.first.empty?
32
+
33
+ constant = Object
34
+ names.each do |name|
35
+ args = Module.method(:const_get).arity != 1 ? [false] : []
36
+
37
+ if constant.const_defined?(name, *args)
38
+ constant = constant.const_get(name)
39
+ else
40
+ constant = constant.const_missing(name)
41
+ end
42
+ end
43
+ constant
44
+ end
45
+ end
@@ -0,0 +1,80 @@
1
+ require "foreman"
2
+ require "shellwords"
3
+
4
+ class Foreman::Process
5
+
6
+ attr_reader :command
7
+ attr_reader :env
8
+
9
+ # Create a Process
10
+ #
11
+ # @param [String] command The command to run
12
+ # @param [Hash] options
13
+ #
14
+ # @option options [String] :cwd (./) Change to this working directory before executing the process
15
+ # @option options [Hash] :env ({}) Environment variables to set for this process
16
+ #
17
+ def initialize(command, options={})
18
+ @command = command
19
+ @options = options.dup
20
+
21
+ @options[:env] ||= {}
22
+ end
23
+
24
+ # Get environment-expanded command for a +Process+
25
+ #
26
+ # @param [Hash] custom_env ({}) Environment variables to merge with defaults
27
+ #
28
+ # @return [String] The expanded command
29
+ #
30
+ def expanded_command(custom_env={})
31
+ env = @options[:env].merge(custom_env)
32
+ expanded_command = command.dup
33
+ env.each do |key, val|
34
+ expanded_command.gsub!("$#{key}", val)
35
+ end
36
+ expanded_command
37
+ end
38
+
39
+ # Run a +Process+
40
+ #
41
+ # @param [Hash] options
42
+ #
43
+ # @option options :env ({}) Environment variables to set for this execution
44
+ # @option options :output ($stdout) The output stream
45
+ #
46
+ # @returns [Fixnum] pid The +pid+ of the process
47
+ #
48
+ def run(options={})
49
+ env = @options[:env].merge(options[:env] || {})
50
+ output = options[:output] || $stdout
51
+ runner = "#{Foreman.runner}".shellescape
52
+
53
+ Dir.chdir(cwd) do
54
+ Process.spawn env, expanded_command(env), :out => output, :err => output
55
+ end
56
+ end
57
+
58
+ # Exec a +Process+
59
+ #
60
+ # @param [Hash] options
61
+ #
62
+ # @option options :env ({}) Environment variables to set for this execution
63
+ #
64
+ # @return Does not return
65
+ def exec(options={})
66
+ env = @options[:env].merge(options[:env] || {})
67
+ env.each { |k, v| ENV[k] = v }
68
+ Dir.chdir(cwd)
69
+ Kernel.exec expanded_command(env)
70
+ end
71
+
72
+ # Returns the working directory for this +Process+
73
+ #
74
+ # @returns [String]
75
+ #
76
+ def cwd
77
+ File.expand_path(@options[:cwd] || ".")
78
+ end
79
+
80
+ end
@@ -0,0 +1,94 @@
1
+ require "foreman"
2
+
3
+ # Reads and writes Procfiles
4
+ #
5
+ # A valid Procfile entry is captured by this regex:
6
+ #
7
+ # /^([A-Za-z0-9_]+):\s*(.+)$/
8
+ #
9
+ # All other lines are ignored.
10
+ #
11
+ class Foreman::Procfile
12
+
13
+ # Initialize a Procfile
14
+ #
15
+ # @param [String] filename (nil) An optional filename to read from
16
+ #
17
+ def initialize(filename=nil)
18
+ @entries = []
19
+ load(filename) if filename
20
+ end
21
+
22
+ # Yield each +Procfile+ entry in order
23
+ #
24
+ def entries(&blk)
25
+ @entries.each do |(name, command)|
26
+ yield name, command
27
+ end
28
+ end
29
+
30
+ # Retrieve a +Procfile+ command by name
31
+ #
32
+ # @param [String] name The name of the Procfile entry to retrieve
33
+ #
34
+ def [](name)
35
+ if entry = @entries.detect { |n,c| name == n }
36
+ entry.last
37
+ end
38
+ end
39
+
40
+ # Create a +Procfile+ entry
41
+ #
42
+ # @param [String] name The name of the +Procfile+ entry to create
43
+ # @param [String] command The command of the +Procfile+ entry to create
44
+ #
45
+ def []=(name, command)
46
+ delete name
47
+ @entries << [name, command]
48
+ end
49
+
50
+ # Remove a +Procfile+ entry
51
+ #
52
+ # @param [String] name The name of the +Procfile+ entry to remove
53
+ #
54
+ def delete(name)
55
+ @entries.reject! { |n,c| name == n }
56
+ end
57
+
58
+ # Load a Procfile from a file
59
+ #
60
+ # @param [String] filename The filename of the +Procfile+ to load
61
+ #
62
+ def load(filename)
63
+ @entries.replace parse(filename)
64
+ end
65
+
66
+ # Save a Procfile to a file
67
+ #
68
+ # @param [String] filename Save the +Procfile+ to this file
69
+ #
70
+ def save(filename)
71
+ File.open(filename, 'w') do |file|
72
+ file.puts self.to_s
73
+ end
74
+ end
75
+
76
+ # Get the +Procfile+ as a +String+
77
+ #
78
+ def to_s
79
+ @entries.map do |name, command|
80
+ [ name, command ].join(": ")
81
+ end.join("\n")
82
+ end
83
+
84
+ private
85
+
86
+ def parse(filename)
87
+ File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
88
+ if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
89
+ [$1, $2]
90
+ end
91
+ end.compact
92
+ end
93
+
94
+ end
@@ -0,0 +1,5 @@
1
+ module Foreman
2
+
3
+ VERSION = "0.81.0"
4
+
5
+ end
data/lib/foreman.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "foreman/version"
2
+
3
+ module Foreman
4
+
5
+ def self.runner
6
+ File.expand_path("../../bin/foreman-runner", __FILE__)
7
+ end
8
+
9
+ def self.ruby_18?
10
+ defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
11
+ end
12
+
13
+ def self.windows?
14
+ defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
15
+ end
16
+
17
+ end