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
@@ -3,94 +3,83 @@ require "rubygems"
3
3
 
4
4
  class Foreman::Process
5
5
 
6
- attr_reader :entry
7
- attr_reader :num
8
- attr_reader :pid
9
- attr_reader :port
6
+ attr_reader :command
7
+ attr_reader :env
10
8
 
11
- def initialize(entry, num, port)
12
- @entry = entry
13
- @num = num
14
- @port = port
15
- end
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
16
20
 
17
- def run(pipe, basedir, environment)
18
- with_environment(environment.merge("PORT" => port.to_s)) do
19
- run_process basedir, entry.command, pipe
20
- end
21
+ @options[:env] ||= {}
21
22
  end
22
23
 
23
- def name
24
- "%s.%s" % [ entry.name, num ]
24
+ # Run a +Process+
25
+ #
26
+ # @param [Hash] options
27
+ #
28
+ # @option options :env ({}) Environment variables to set for this execution
29
+ # @option options :output ($stdout) The output stream
30
+ #
31
+ # @returns [Fixnum] pid The +pid+ of the process
32
+ #
33
+ def run(options={})
34
+ env = options[:env] ? @options[:env].merge(options[:env]) : @options[:env]
35
+ output = options[:output] || $stdout
36
+
37
+ if Foreman.windows?
38
+ Dir.chdir(cwd) do
39
+ Process.spawn env, command, :out => output, :err => output, :new_pgroup => true
40
+ end
41
+ elsif Foreman.jruby?
42
+ Dir.chdir(cwd) do
43
+ require "posix/spawn"
44
+ POSIX::Spawn.spawn env, command, :out => output, :err => output, :pgroup => 0
45
+ end
46
+ else
47
+ Dir.chdir(cwd) do
48
+ Process.spawn env, command, :out => output, :err => output, :pgroup => 0
49
+ end
50
+ end
25
51
  end
26
52
 
53
+ # Send a signal to this +Process+
54
+ #
55
+ # @param [String] signal The signal to send
56
+ #
27
57
  def kill(signal)
28
- pid && Process.kill(signal, pid)
58
+ pid && Process.kill(signal, -1 * pid)
29
59
  rescue Errno::ESRCH
30
60
  false
31
61
  end
32
62
 
33
- def detach
34
- pid && Process.detach(pid)
35
- end
36
-
63
+ # Test whether or not this +Process+ is still running
64
+ #
65
+ # @returns [Boolean]
66
+ #
37
67
  def alive?
38
68
  kill(0)
39
69
  end
40
70
 
71
+ # Test whether or not this +Process+ has terminated
72
+ #
73
+ # @returns [Boolean]
74
+ #
41
75
  def dead?
42
76
  !alive?
43
77
  end
44
78
 
45
79
  private
46
80
 
47
- def fork_with_io(command, basedir)
48
- reader, writer = IO.pipe
49
- command = replace_command_env(command)
50
- pid = if Foreman.windows?
51
- Dir.chdir(basedir) do
52
- Process.spawn command, :out => writer, :err => writer
53
- end
54
- elsif Foreman.jruby?
55
- require "posix/spawn"
56
- POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
57
- :out => writer, :err => writer
58
- })
59
- else
60
- fork do
61
- writer.sync = true
62
- $stdout.reopen writer
63
- $stderr.reopen writer
64
- reader.close
65
- exec Foreman.runner, "-d", basedir, command
66
- end
67
- end
68
- [ reader, pid ]
69
- end
70
-
71
- def run_process(basedir, command, pipe)
72
- io, @pid = fork_with_io(command, basedir)
73
- output pipe, "started with pid %d" % @pid
74
- Thread.new do
75
- until io.eof?
76
- output pipe, io.gets
77
- end
78
- end
79
- end
80
-
81
- def output(pipe, message)
82
- pipe.puts "%s,%s" % [ name, message ]
81
+ def cwd
82
+ @options[:cwd] || "."
83
83
  end
84
84
 
85
- def replace_command_env(command)
86
- command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
87
- end
88
-
89
- def with_environment(environment)
90
- original = ENV.to_hash
91
- ENV.update environment
92
- yield
93
- ensure
94
- ENV.replace original
95
- end
96
85
  end
@@ -1,56 +1,90 @@
1
1
  require "foreman"
2
- require "foreman/procfile_entry"
3
2
 
4
- # A valid Procfile entry is captured by this regex.
5
- # All other lines are ignored.
3
+ # Reads and writes Procfiles
4
+ #
5
+ # A valid Procfile entry is captured by this regex:
6
6
  #
7
- # /^([A-Za-z0-9_]+):\s*(.+)$/
7
+ # /^([A-Za-z0-9_]+):\s*(.+)$/
8
8
  #
9
- # $1 = name
10
- # $2 = command
9
+ # All other lines are ignored.
11
10
  #
12
11
  class Foreman::Procfile
13
12
 
14
- attr_reader :entries
15
-
13
+ # Initialize a Procfile
14
+ #
15
+ # @param [String] filename (nil) An optional filename to read from
16
+ #
16
17
  def initialize(filename=nil)
17
18
  @entries = []
18
19
  load(filename) if filename
19
20
  end
20
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
+ #
21
34
  def [](name)
22
- entries.detect { |entry| entry.name == name }
35
+ @entries.detect { |n,c| name == n }.last
36
+ end
37
+
38
+ # Create a +Procfile+ entry
39
+ #
40
+ # @param [String] name The name of the +Procfile+ entry to create
41
+ # @param [String] command The command of the +Procfile+ entry to create
42
+ #
43
+ def []=(name, command)
44
+ delete name
45
+ @entries << [name, command]
23
46
  end
24
47
 
25
- def process_names
26
- entries.map(&:name)
48
+ # Remove a +Procfile+ entry
49
+ #
50
+ # @param [String] name The name of the +Procfile+ entry to remove
51
+ #
52
+ def delete(name)
53
+ @entries.reject! { |n,c| name == n }
27
54
  end
28
55
 
56
+ # Load a Procfile from a file
57
+ #
58
+ # @param [String] filename The filename of the +Procfile+ to load
59
+ #
29
60
  def load(filename)
30
- entries.clear
31
- parse_procfile(filename)
61
+ @entries.replace parse(filename)
32
62
  end
33
63
 
34
- def write(filename)
35
- File.open(filename, 'w') do |io|
36
- entries.each do |ent|
37
- io.puts(ent)
38
- end
64
+ # Save a Procfile to a file
65
+ #
66
+ # @param [String] filename Save the +Procfile+ to this file
67
+ #
68
+ def save(filename)
69
+ File.open(filename, 'w') do |file|
70
+ file.puts self.to_s
39
71
  end
40
72
  end
41
73
 
42
- def <<(entry)
43
- entries << Foreman::ProcfileEntry.new(*entry)
44
- self
74
+ # Get the +Procfile+ as a +String+
75
+ #
76
+ def to_s
77
+ @entries.map do |name, command|
78
+ [ name, command ].join(": ")
79
+ end.join("\n")
45
80
  end
46
81
 
82
+ private
47
83
 
48
- protected
49
-
50
- def parse_procfile(filename)
84
+ def parse(filename)
51
85
  File.read(filename).split("\n").map do |line|
52
86
  if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
53
- self << [ $1, $2 ]
87
+ [$1, $2]
54
88
  end
55
89
  end.compact
56
90
  end
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.46.0"
3
+ VERSION = "0.48.0.pre1"
4
4
 
5
5
  end
data/man/foreman.1 CHANGED
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "FOREMAN" "1" "April 2012" "Foreman 0.45.0" "Foreman Manual"
4
+ .TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBforeman\fR \- manage Procfile\-based applications
@@ -46,6 +46,10 @@ Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
46
46
  \fB\-p\fR, \fB\-\-port\fR
47
47
  Specify which port to use as the base for this application\. Should be a multiple of 1000\.
48
48
  .
49
+ .TP
50
+ \fB\-t\fR, \fB\-\-tmux\fR
51
+ Runs the processes in a tmux session\. Creates one window for each process and an extra window containing the output of each window (requires gawk)\.
52
+ .
49
53
  .P
50
54
  \fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
51
55
  .
@@ -3,188 +3,74 @@ require "foreman/cli"
3
3
 
4
4
  describe "Foreman::CLI", :fakefs do
5
5
  subject { Foreman::CLI.new }
6
- let(:engine) { subject.send(:engine) }
7
- let(:entries) { engine.procfile.entries.inject({}) { |h,e| h.update(e.name => e) } }
8
6
 
9
- describe "start" do
10
- describe "with a non-existent Procfile" do
11
- it "prints an error" do
12
- mock_error(subject, "Procfile does not exist.") do
13
- dont_allow.instance_of(Foreman::Engine).start
14
- subject.start
15
- end
16
- end
17
- end
7
+ describe ".foreman" do
8
+ before { File.open(".foreman", "w") { |f| f.puts "formation: alpha=2" } }
18
9
 
19
- describe "with a Procfile" do
20
- before(:each) { write_procfile }
21
-
22
- it "runs successfully" do
23
- dont_allow(subject).error
24
- mock.instance_of(Foreman::Engine).start
25
- subject.start
26
- end
27
-
28
- it "can run a single process" do
29
- dont_allow(subject).error
30
- stub(engine).watch_for_output
31
- stub(engine).watch_for_termination
32
- mock(entries["alpha"]).spawn(1, is_a(IO), engine.directory, {}, 5000) { [] }
33
- mock(entries["bravo"]).spawn(0, is_a(IO), engine.directory, {}, 5100) { [] }
34
- subject.start("alpha")
35
- end
10
+ it "provides default options" do
11
+ subject.send(:options)["formation"].should == "alpha=2"
36
12
  end
37
13
 
38
- describe "with an alternate root" do
39
- it "reads the Procfile from that root" do
40
- write_procfile "/some/app/Procfile"
41
- mock(Foreman::Procfile).new("/some/app/Procfile")
42
- mock.instance_of(Foreman::Engine).start
43
- foreman %{ start -d /some/app }
44
- end
14
+ it "is overridden by options at the cli" do
15
+ subject = Foreman::CLI.new([], :formation => "alpha=3")
16
+ subject.send(:options)["formation"].should == "alpha=3"
45
17
  end
46
18
  end
47
19
 
48
- describe "export" do
49
- describe "options" do
50
- it "uses .foreman" do
51
- write_procfile
52
- File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
53
- mock_export = mock(Foreman::Export::Upstart)
54
- mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
55
- mock_export.export
56
- foreman %{ export upstart /upstart }
57
- end
58
-
59
- it "respects --env" do
60
- write_procfile
61
- write_env("envfile")
62
- mock_export = mock(Foreman::Export::Upstart)
63
- mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
64
- mock_export.export
65
- foreman %{ export upstart /upstart --env envfile }
66
- end
67
- end
68
-
69
- describe "with a non-existent Procfile" do
70
- it "prints an error" do
20
+ describe "start" do
21
+ describe "when a Procfile doesnt exist", :fakefs do
22
+ it "displays an error" do
71
23
  mock_error(subject, "Procfile does not exist.") do
72
- dont_allow.instance_of(Foreman::Engine).export
73
- subject.export("testapp")
24
+ dont_allow.instance_of(Foreman::Engine).start
25
+ subject.start
74
26
  end
75
27
  end
76
28
  end
77
29
 
78
- describe "with a Procfile" do
79
- before(:each) { write_procfile }
80
-
81
- describe "with a formatter with a generic error" do
82
- before do
83
- mock(Foreman::Export).formatter("errorful") { Class.new(Foreman::Export::Base) do
84
- def export
85
- raise Foreman::Export::Exception.new("foo")
86
- end
87
- end }
88
- end
89
-
90
- it "prints an error" do
91
- mock_error(subject, "foo") do
92
- subject.export("errorful")
93
- end
30
+ describe "with a valid Procfile" do
31
+ it "can run a single command" do
32
+ without_fakefs do
33
+ output = foreman("start env -f #{resource_path("Procfile")}")
34
+ output.should =~ /env.1/
35
+ output.should_not =~ /test.1/
94
36
  end
95
37
  end
96
38
 
97
- describe "with a valid config" do
98
- before(:each) { write_foreman_config("testapp") }
99
-
100
- it "runs successfully" do
101
- dont_allow(subject).error
102
- mock_export = mock(Foreman::Export::Upstart)
103
- mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
104
- mock_export.export
105
- subject.export("upstart", "/tmp/foo")
39
+ it "can run all commands" do
40
+ without_fakefs do
41
+ output = foreman("start -f #{resource_path("Procfile")} -e #{resource_path(".env")}")
42
+ output.should =~ /echo.1 \| echoing/
43
+ output.should =~ /env.1 \| bar/
44
+ output.should =~ /test.1 \| testing/
106
45
  end
107
46
  end
108
47
  end
109
48
  end
110
49
 
111
50
  describe "check" do
112
- describe "with a valid Procfile" do
113
- before { write_procfile }
114
-
115
- it "displays the jobs" do
116
- mock(subject).puts("valid procfile detected (alpha, bravo)")
117
- subject.check
118
- end
51
+ it "with a valid Procfile displays the jobs" do
52
+ write_procfile
53
+ foreman("check").should == "valid procfile detected (alpha, bravo)\n"
119
54
  end
120
55
 
121
- describe "with a blank Procfile" do
122
- before do
123
- FileUtils.touch("Procfile")
124
- end
125
-
126
- it "displays an error" do
127
- mock_error(subject, "no processes defined") do
128
- subject.check
129
- end
130
- end
56
+ it "with a blank Procfile displays an error" do
57
+ FileUtils.touch "Procfile"
58
+ foreman("check").should == "ERROR: no processes defined\n"
131
59
  end
132
60
 
133
- describe "without a Procfile" do
134
- it "displays an error" do
135
- mock_error(subject, "Procfile does not exist.") do
136
- subject.check
137
- end
138
- end
61
+ it "without a Procfile displays an error" do
62
+ FileUtils.rm_f "Procfile"
63
+ foreman("check").should == "ERROR: Procfile does not exist.\n"
139
64
  end
140
65
  end
141
66
 
142
67
  describe "run" do
143
- describe "with a valid Procfile" do
144
- before { write_procfile }
145
-
146
- describe "and a command" do
147
- let(:command) { ["ls", "-l"] }
148
-
149
- before(:each) do
150
- stub(subject).exec
151
- end
152
-
153
- it "should load the environment file" do
154
- write_env
155
- preserving_env do
156
- subject.run *command
157
- ENV["FOO"].should == "bar"
158
- end
159
-
160
- ENV["FOO"].should be_nil
161
- end
162
-
163
- it "should runute the command as a string" do
164
- mock(subject).exec(command.join(" "))
165
- subject.run *command
166
- end
167
- end
168
-
169
- describe "and a non-existent command" do
170
- let(:command) { "iuhtngrglhulhdfg" }
171
-
172
- it "should print an error" do
173
- mock_error(subject, "command not found: #{command}") do
174
- subject.run command
175
- end
176
- end
177
- end
178
-
179
- describe "and a non-executable command" do
180
- let(:command) { __FILE__ }
68
+ it "can run a command" do
69
+ forked_foreman("run echo 1").should == "1\n"
70
+ end
181
71
 
182
- it "should print an error" do
183
- mock_error(subject, "not executable: #{command}") do
184
- subject.run command
185
- end
186
- end
187
- end
72
+ it "includes the environment" do
73
+ forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n"
188
74
  end
189
75
  end
190
76