foreman 0.47.0 → 0.48.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/bin/taskman +8 -0
  2. data/data/example/Procfile +4 -3
  3. data/data/example/spawnee +14 -0
  4. data/data/example/spawner +7 -0
  5. data/data/export/bluepill/master.pill.erb +10 -10
  6. data/data/export/launchd/launchd.plist.erb +3 -3
  7. data/data/export/runit/log/run.erb +7 -0
  8. data/data/export/runit/run.erb +2 -2
  9. data/data/export/supervisord/app.conf.erb +12 -12
  10. data/data/export/upstart/master.conf.erb +2 -2
  11. data/data/export/upstart/process.conf.erb +3 -3
  12. data/lib/foreman/cli.rb +49 -21
  13. data/lib/foreman/engine.rb +208 -148
  14. data/lib/foreman/engine/cli.rb +98 -0
  15. data/lib/foreman/env.rb +27 -0
  16. data/lib/foreman/export.rb +0 -1
  17. data/lib/foreman/export/base.rb +58 -35
  18. data/lib/foreman/export/bluepill.rb +3 -17
  19. data/lib/foreman/export/inittab.rb +8 -11
  20. data/lib/foreman/export/launchd.rb +4 -16
  21. data/lib/foreman/export/runit.rb +14 -39
  22. data/lib/foreman/export/supervisord.rb +3 -13
  23. data/lib/foreman/export/upstart.rb +9 -27
  24. data/lib/foreman/process.rb +56 -67
  25. data/lib/foreman/procfile.rb +59 -25
  26. data/lib/foreman/version.rb +1 -1
  27. data/man/foreman.1 +4 -0
  28. data/spec/foreman/cli_spec.rb +38 -152
  29. data/spec/foreman/engine_spec.rb +46 -80
  30. data/spec/foreman/export/base_spec.rb +4 -7
  31. data/spec/foreman/export/bluepill_spec.rb +7 -6
  32. data/spec/foreman/export/inittab_spec.rb +7 -7
  33. data/spec/foreman/export/launchd_spec.rb +4 -7
  34. data/spec/foreman/export/runit_spec.rb +12 -17
  35. data/spec/foreman/export/supervisord_spec.rb +7 -56
  36. data/spec/foreman/export/upstart_spec.rb +18 -23
  37. data/spec/foreman/process_spec.rb +27 -124
  38. data/spec/foreman/procfile_spec.rb +26 -16
  39. data/spec/resources/Procfile +4 -0
  40. data/spec/resources/bin/echo +2 -0
  41. data/spec/resources/bin/env +2 -0
  42. data/spec/resources/bin/test +2 -0
  43. data/spec/resources/export/bluepill/app-concurrency.pill +4 -4
  44. data/spec/resources/export/bluepill/app.pill +4 -4
  45. data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
  46. data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
  47. data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
  48. data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
  49. data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
  50. data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
  51. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  52. data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
  53. data/spec/spec_helper.rb +58 -6
  54. metadata +24 -22
  55. data/data/export/runit/log_run.erb +0 -7
  56. data/lib/foreman/color.rb +0 -40
  57. data/lib/foreman/procfile_entry.rb +0 -26
  58. data/lib/foreman/utils.rb +0 -18
  59. data/spec/foreman/color_spec.rb +0 -31
  60. data/spec/foreman/procfile_entry_spec.rb +0 -13
  61. data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
  62. data/spec/resources/export/supervisord/app-env.conf +0 -21
  63. data/spec/resources/export/supervisord/app.conf +0 -24
@@ -5,141 +5,44 @@ require 'timeout'
5
5
  require 'tmpdir'
6
6
 
7
7
  describe Foreman::Process do
8
- subject { described_class.new entry, number, port }
9
8
 
10
- let(:number) { 1 }
11
- let(:port) { 777 }
12
- let(:command) { "script" }
13
- let(:name) { "foobar" }
14
- let(:entry) { OpenStruct.new :name => name, :command => command }
15
-
16
- its(:entry) { entry }
17
- its(:num) { number }
18
- its(:port) { port }
19
- its(:name) { "#{name}.#{port}" }
20
- its(:pid) { nil }
21
-
22
- describe '#run' do
23
- let(:pipe) { :pipe }
24
- let(:basedir) { Dir.mktmpdir }
25
- let(:env) {{ 'foo' => 'bar' }}
26
- let(:init_delta) { 0.1 }
9
+ def run(process, options={})
10
+ rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
11
+ process.run(options.merge(:output => wr))
12
+ rd.gets
13
+ end
27
14
 
28
- after { FileUtils.remove_entry_secure basedir }
15
+ describe "#run" do
29
16
 
30
- def run(cmd=command)
31
- entry.command = cmd
32
- subject.run pipe, basedir, env
33
- subject.detach && sleep(init_delta)
17
+ it "runs the process" do
18
+ process = Foreman::Process.new(resource_path("bin/test"))
19
+ run(process).should == "testing\n"
34
20
  end
35
21
 
36
- def run_file(executable, code)
37
- file = File.open("#{basedir}/script", 'w') {|it| it << code }
38
- run "#{executable} #{file.path}"
39
- sleep 1
22
+ it "can set environment" do
23
+ process = Foreman::Process.new(resource_path("bin/env FOO"), :env => { "FOO" => "bar" })
24
+ run(process).should == "bar\n"
40
25
  end
41
26
 
42
- context 'options' do
43
- it 'should set PORT for environment' do
44
- mock(subject).run_process(basedir, command, pipe) do
45
- ENV['PORT'].should == port.to_s
46
- end
47
- run
48
- end
49
-
50
- it 'should set custom variables for environment' do
51
- mock(subject).run_process(basedir, command, pipe) do
52
- ENV['foo'].should == 'bar'
53
- end
54
- run
55
- end
56
-
57
- it 'should restore environment afterwards' do
58
- mock(subject).run_process(basedir, command, pipe)
59
- run
60
- ENV.should_not include('PORT', 'foo')
61
- end
27
+ it "can set per-run environment" do
28
+ process = Foreman::Process.new(resource_path("bin/env FOO"))
29
+ run(process, :env => { "FOO" => "bar "}).should == "bar\n"
62
30
  end
63
31
 
64
- context 'process' do
65
- around do |spec|
66
- IO.pipe do |reader, writer|
67
- @reader, @writer = reader, writer
68
- spec.run
69
- end
70
- end
71
-
72
- let(:pipe) { @writer }
73
- let(:output) { @reader.read_nonblock 1024 }
74
-
75
- it 'should not block' do
76
- expect {
77
- Timeout.timeout(2*init_delta) { run 'sleep 2' }
78
- }.should_not raise_exception
79
- end
80
-
81
- it 'should be alive' do
82
- run 'sleep 1'
83
- subject.should be_alive
84
- end
85
-
86
- it 'should be dead' do
87
- run 'exit'
88
- subject.should be_dead
89
- end
90
-
91
- it 'should be killable' do
92
- run 'sleep 1'
93
- subject.kill 'TERM'
94
- subject.should be_dead
95
- end
96
-
97
- it 'should send different signals' do
98
- run_file 'ruby', <<-CODE
99
- trap "TERM", "IGNORE"
100
- loop { sleep 1 }
101
- CODE
102
- subject.should be_alive
103
- subject.kill 'TERM'
104
- subject.should be_alive
105
- subject.kill 'KILL'
106
- subject.should be_dead
107
- end
108
-
109
- it 'should redirect stdout' do
110
- run 'echo hey'
111
- output.should include('hey')
112
- end
113
-
114
- it 'should redirect stderr' do
115
- run 'echo hey >2'
116
- output.should include('hey')
117
- end
118
-
119
- it 'should handle variables' do
120
- run 'echo $PORT'
121
- output.should include('777')
122
- end
123
-
124
- it 'should handle multi-word arguments (old test)' do
125
- # TODO: This test used to be marked pending; it now passes,
126
- # but is very slow. The next test is a fast replacement.
127
- run %{ sh -c "trap '' TERM; sleep 10" }
128
- subject.should be_alive
129
- end
32
+ it "can handle env vars in the command" do
33
+ process = Foreman::Process.new(resource_path("bin/echo $FOO"), :env => { "FOO" => "bar" })
34
+ run(process).should == "bar\n"
35
+ end
130
36
 
131
- it 'should handle multi-word arguments' do
132
- # We have to be a little clever here since Foreman will always
133
- # print a status message that includes the command.
134
- run %{ sh -c 'echo abcdef | tr a-c x | tr d-f y' }
135
- output.should include('xxxyyy')
136
- end
37
+ it "can handle per-run env vars in the command" do
38
+ process = Foreman::Process.new(resource_path("bin/echo $FOO"))
39
+ run(process, :env => { "FOO" => "bar" }).should == "bar\n"
40
+ end
137
41
 
138
- it 'should not clobber "$x"-subexpressions' do
139
- pending 'this conflicts with the variable interpolation hack'
140
- run %{ sh -c 'echo \$abcdef | tr \$ %' }
141
- output.should include('%abcdef')
142
- end
42
+ it "should output utf8 properly" do
43
+ process = Foreman::Process.new(resource_path("bin/utf8"))
44
+ run(process).should == "\xFF\x03\n"
143
45
  end
144
46
  end
47
+
145
48
  end
@@ -3,29 +3,39 @@ require 'foreman/procfile'
3
3
  require 'pathname'
4
4
  require 'tmpdir'
5
5
 
6
- describe Foreman::Procfile do
7
- subject { described_class.new }
6
+ describe Foreman::Procfile, :fakefs do
7
+ subject { Foreman::Procfile.new }
8
8
 
9
- let(:testdir) { Pathname(Dir.tmpdir) }
10
- let(:procfile) { testdir + 'Procfile' }
9
+ it "can load from a file" do
10
+ write_procfile
11
+ subject.load "Procfile"
12
+ subject["alpha"].should == "./alpha"
13
+ subject["bravo"].should == "./bravo"
14
+ end
15
+
16
+ it "loads a passed-in Procfile" do
17
+ write_procfile
18
+ procfile = Foreman::Procfile.new("Procfile")
19
+ procfile["alpha"].should == "./alpha"
20
+ procfile["bravo"].should == "./bravo"
21
+ end
11
22
 
12
23
  it "can have a process appended to it" do
13
- subject << ['alpha', './alpha']
14
- subject['alpha'].should be_a(Foreman::ProcfileEntry)
24
+ subject["charlie"] = "./charlie"
25
+ subject["charlie"].should == "./charlie"
15
26
  end
16
27
 
17
- it "can write itself out to a file" do
18
- subject << ['alpha', './alpha']
19
- subject.write(procfile)
20
- procfile.read.should == "alpha: ./alpha\n"
28
+ it "can write to a string" do
29
+ subject["foo"] = "./foo"
30
+ subject["bar"] = "./bar"
31
+ subject.to_s.should == "foo: ./foo\nbar: ./bar"
21
32
  end
22
33
 
23
- it "can re-read entries from a file" do
24
- procfile.open('w') { |io| io.puts "gamma: ./radiation", "theta: ./rate" }
25
- subject << ['alpha', './alpha']
26
- subject.load(procfile)
27
- subject.process_names.should have(2).members
28
- subject.process_names.should include('gamma', 'theta')
34
+ it "can write to a file" do
35
+ subject["foo"] = "./foo"
36
+ subject["bar"] = "./bar"
37
+ subject.save "/tmp/proc"
38
+ File.read("/tmp/proc").should == "foo: ./foo\nbar: ./bar\n"
29
39
  end
30
40
 
31
41
  end
@@ -0,0 +1,4 @@
1
+ echo: bin/echo echoing
2
+ env: bin/env FOO
3
+ test: bin/test
4
+ utf8: bin/utf8
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ echo $*
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo ${!1}
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ echo "testing"
@@ -11,14 +11,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
11
11
 
12
12
  process.working_dir = "/tmp/app"
13
13
  process.daemonize = true
14
- process.environment = {"PORT" => "5000"}
14
+ process.environment = {"PORT"=>"5000"}
15
15
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
16
  process.stop_grace_time = 45.seconds
17
17
 
18
18
  process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
19
19
 
20
20
  process.monitor_children do |children|
21
- children.stop_command "kill -QUIT {{PID}}"
21
+ children.stop_command "kill {{PID}}"
22
22
  end
23
23
 
24
24
  process.group = "app-alpha"
@@ -30,14 +30,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
30
30
 
31
31
  process.working_dir = "/tmp/app"
32
32
  process.daemonize = true
33
- process.environment = {"PORT" => "5001"}
33
+ process.environment = {"PORT"=>"5001"}
34
34
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
35
35
  process.stop_grace_time = 45.seconds
36
36
 
37
37
  process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
38
38
 
39
39
  process.monitor_children do |children|
40
- children.stop_command "kill -QUIT {{PID}}"
40
+ children.stop_command "kill {{PID}}"
41
41
  end
42
42
 
43
43
  process.group = "app-alpha"
@@ -11,14 +11,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
11
11
 
12
12
  process.working_dir = "/tmp/app"
13
13
  process.daemonize = true
14
- process.environment = {"PORT" => "5000"}
14
+ process.environment = {"PORT"=>"5000"}
15
15
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
16
  process.stop_grace_time = 45.seconds
17
17
 
18
18
  process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
19
19
 
20
20
  process.monitor_children do |children|
21
- children.stop_command "kill -QUIT {{PID}}"
21
+ children.stop_command "kill {{PID}}"
22
22
  end
23
23
 
24
24
  process.group = "app-alpha"
@@ -29,14 +29,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
29
29
 
30
30
  process.working_dir = "/tmp/app"
31
31
  process.daemonize = true
32
- process.environment = {"PORT" => "5100"}
32
+ process.environment = {"PORT"=>"5100"}
33
33
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
34
34
  process.stop_grace_time = 45.seconds
35
35
 
36
36
  process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
37
37
 
38
38
  process.monitor_children do |children|
39
- children.stop_command "kill -QUIT {{PID}}"
39
+ children.stop_command "kill {{PID}}"
40
40
  end
41
41
 
42
42
  process.group = "app-bravo"
@@ -0,0 +1,24 @@
1
+
2
+ [program:app-alpha-1]
3
+ command=./alpha
4
+ autostart=true
5
+ autorestart=true
6
+ stopsignal=QUIT
7
+ stdout_logfile=/var/log/app/alpha-1.log
8
+ stderr_logfile=/var/log/app/alpha-1.error.log
9
+ user=app
10
+ directory=/tmp/app
11
+ environment=PORT="5000"
12
+ [program:app-bravo-1]
13
+ command=./bravo
14
+ autostart=true
15
+ autorestart=true
16
+ stopsignal=QUIT
17
+ stdout_logfile=/var/log/app/bravo-1.log
18
+ stderr_logfile=/var/log/app/bravo-1.error.log
19
+ user=app
20
+ directory=/tmp/app
21
+ environment=PORT="5100"
22
+
23
+ [group:app]
24
+ programs=app-alpha-1,app-bravo-1
@@ -4,8 +4,8 @@ command=./alpha
4
4
  autostart=true
5
5
  autorestart=true
6
6
  stopsignal=QUIT
7
- stdout_logfile=/var/log/app/alpha-1-out.log
8
- stderr_logfile=/var/log/app/alpha-1-err.log
7
+ stdout_logfile=/var/log/app/alpha-1.log
8
+ stderr_logfile=/var/log/app/alpha-1.error.log
9
9
  user=app
10
10
  directory=/tmp/app
11
11
  environment=PORT="5000"
@@ -14,8 +14,8 @@ command=./alpha
14
14
  autostart=true
15
15
  autorestart=true
16
16
  stopsignal=QUIT
17
- stdout_logfile=/var/log/app/alpha-2-out.log
18
- stderr_logfile=/var/log/app/alpha-2-err.log
17
+ stdout_logfile=/var/log/app/alpha-2.log
18
+ stderr_logfile=/var/log/app/alpha-2.error.log
19
19
  user=app
20
20
  directory=/tmp/app
21
21
  environment=PORT="5001"
data/spec/spec_helper.rb CHANGED
@@ -6,6 +6,7 @@ SimpleCov.start do
6
6
  end
7
7
 
8
8
  require "rspec"
9
+ require "timecop"
9
10
  require "fakefs/safe"
10
11
  require "fakefs/spec_helpers"
11
12
 
@@ -23,7 +24,39 @@ def mock_error(subject, message)
23
24
  end
24
25
 
25
26
  def foreman(args)
26
- Foreman::CLI.start(args.split(" "))
27
+ capture_stdout do
28
+ begin
29
+ Foreman::CLI.start(args.split(" "))
30
+ rescue SystemExit
31
+ end
32
+ end
33
+ end
34
+
35
+ def forked_foreman(args)
36
+ rd, wr = IO.pipe("BINARY")
37
+ Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr)
38
+ wr.close
39
+ rd.read
40
+ end
41
+
42
+ def fork_and_capture(&blk)
43
+ rd, wr = IO.pipe("BINARY")
44
+ pid = fork do
45
+ rd.close
46
+ wr.sync = true
47
+ $stdout.reopen wr
48
+ $stderr.reopen wr
49
+ blk.call
50
+ $stdout.flush
51
+ $stdout.close
52
+ end
53
+ wr.close
54
+ Process.wait pid
55
+ buffer = ""
56
+ until rd.eof?
57
+ p [:foo]
58
+ buffer += rd.gets
59
+ end
27
60
  end
28
61
 
29
62
  def mock_exit(&block)
@@ -55,13 +88,21 @@ def write_env(env=".env", options={"FOO"=>"bar"})
55
88
  end
56
89
  end
57
90
 
58
- def load_export_templates_into_fakefs(type)
91
+ def without_fakefs
59
92
  FakeFS.deactivate!
60
- files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
61
- hash.update(file => File.read(file))
62
- end
93
+ ret = yield
63
94
  FakeFS.activate!
64
- files.each do |filename, contents|
95
+ ret
96
+ end
97
+
98
+ def load_export_templates_into_fakefs(type)
99
+ without_fakefs do
100
+ Dir[File.expand_path("../../data/export/#{type}/**/*", __FILE__)].inject({}) do |hash, file|
101
+ next(hash) if File.directory?(file)
102
+ hash.update(file => File.read(file))
103
+ end
104
+ end.each do |filename, contents|
105
+ FileUtils.mkdir_p File.dirname(filename)
65
106
  File.open(filename, "w") do |f|
66
107
  f.puts contents
67
108
  end
@@ -93,6 +134,17 @@ def normalize_space(s)
93
134
  s.gsub(/\n[\n\s]*/, "\n")
94
135
  end
95
136
 
137
+ def capture_stdout
138
+ old_stdout = $stdout.dup
139
+ rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
140
+ $stdout = wr
141
+ yield
142
+ wr.close
143
+ rd.read
144
+ ensure
145
+ $stdout = old_stdout
146
+ end
147
+
96
148
  RSpec.configure do |config|
97
149
  config.treat_symbols_as_metadata_keys_with_true_values = true
98
150
  config.color_enabled = true