foreman 0.26.1 → 0.40.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 (41) hide show
  1. data/README.md +40 -0
  2. data/bin/foreman-runner +36 -0
  3. data/data/example/Procfile +1 -0
  4. data/data/example/utf8 +11 -0
  5. data/data/export/bluepill/master.pill.erb +3 -3
  6. data/lib/foreman/cli.rb +35 -31
  7. data/lib/foreman/engine.rb +109 -82
  8. data/lib/foreman/export/base.rb +12 -5
  9. data/lib/foreman/export/bluepill.rb +5 -7
  10. data/lib/foreman/export/inittab.rb +11 -13
  11. data/lib/foreman/export/runit.rb +24 -25
  12. data/lib/foreman/export/upstart.rb +9 -11
  13. data/lib/foreman/export.rb +21 -0
  14. data/lib/foreman/helpers.rb +45 -0
  15. data/lib/foreman/process.rb +88 -6
  16. data/lib/foreman/procfile.rb +8 -7
  17. data/lib/foreman/procfile_entry.rb +22 -0
  18. data/lib/foreman/utils.rb +4 -1
  19. data/lib/foreman/version.rb +1 -1
  20. data/lib/foreman.rb +12 -1
  21. data/man/foreman.1 +17 -3
  22. data/spec/foreman/cli_spec.rb +96 -7
  23. data/spec/foreman/engine_spec.rb +52 -31
  24. data/spec/foreman/export/base_spec.rb +22 -0
  25. data/spec/foreman/export/bluepill_spec.rb +23 -7
  26. data/spec/foreman/export/inittab_spec.rb +40 -0
  27. data/spec/foreman/export/runit_spec.rb +18 -12
  28. data/spec/foreman/export/upstart_spec.rb +40 -8
  29. data/spec/foreman/export_spec.rb +22 -0
  30. data/spec/foreman/helpers_spec.rb +26 -0
  31. data/spec/foreman/process_spec.rb +131 -2
  32. data/spec/foreman_spec.rb +5 -0
  33. data/spec/helper_spec.rb +18 -0
  34. data/spec/resources/bin/utf8 +2 -0
  35. data/spec/resources/export/bluepill/app-concurrency.pill +47 -0
  36. data/spec/resources/export/bluepill/app.pill +1 -22
  37. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  38. data/spec/resources/export/inittab/inittab.default +4 -0
  39. data/spec/spec_helper.rb +36 -4
  40. metadata +23 -11
  41. data/README.markdown +0 -49
@@ -1,9 +1,16 @@
1
1
  require "spec_helper"
2
2
  require "foreman/engine"
3
3
 
4
- describe "Foreman::Engine" do
4
+ describe "Foreman::Engine", :fakefs do
5
5
  subject { Foreman::Engine.new("Procfile", {}) }
6
6
 
7
+ before do
8
+ any_instance_of(Foreman::Engine) do |engine|
9
+ stub(engine).proctitle
10
+ stub(engine).termtitle
11
+ end
12
+ end
13
+
7
14
  describe "initialize" do
8
15
  describe "without an existing Procfile" do
9
16
  it "raises an error" do
@@ -24,8 +31,9 @@ describe "Foreman::Engine" do
24
31
  describe "start" do
25
32
  it "forks the processes" do
26
33
  write_procfile
27
- mock(subject).fork(subject.procfile["alpha"])
28
- mock(subject).fork(subject.procfile["bravo"])
34
+ mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO))
35
+ mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO))
36
+ mock(subject).watch_for_output
29
37
  mock(subject).watch_for_termination
30
38
  subject.start
31
39
  end
@@ -33,52 +41,49 @@ describe "Foreman::Engine" do
33
41
  it "handles concurrency" do
34
42
  write_procfile
35
43
  engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
36
- mock(engine).fork_individual(engine.procfile["alpha"], 1, 5000)
37
- mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
38
- mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
44
+ mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO)).twice
45
+ mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never
46
+ mock(engine).watch_for_output
39
47
  mock(engine).watch_for_termination
40
48
  engine.start
41
49
  end
42
50
  end
43
51
 
44
- describe "execute" do
45
- it "runs the processes" do
46
- write_procfile
47
- mock(subject).fork(subject.procfile["alpha"])
48
- mock(subject).watch_for_termination
49
- subject.execute("alpha")
50
- end
51
-
52
- it "shows an error running a process that doesnt exist" do
53
- write_procfile
54
- mock(subject).puts("ERROR: no such process: foo")
55
- lambda { subject.execute("foo") }.should raise_error(SystemExit)
56
- end
57
- end
58
-
59
52
  describe "environment" do
60
53
  before(:each) do
61
54
  write_procfile
62
55
  stub(Process).fork
56
+ any_instance_of(Foreman::Engine) do |engine|
57
+ stub(engine).info
58
+ stub(engine).spawn_processes
59
+ stub(engine).watch_for_termination
60
+ end
63
61
  end
64
62
 
65
63
  it "should read if specified" do
66
64
  File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
67
65
  engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
68
- stub(engine).info
69
- mock(engine).watch_for_termination
70
66
  engine.environment.should == {"FOO"=>"baz"}
71
- engine.execute("alpha")
67
+ engine.start
72
68
  end
73
69
 
74
70
  it "should read more than one if specified" do
75
71
  File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
76
72
  File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
77
73
  engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
78
- stub(engine).info
79
- mock(engine).watch_for_termination
80
74
  engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
81
- engine.execute("alpha")
75
+ engine.start
76
+ end
77
+
78
+ it "should handle quoted values" do
79
+ File.open("/tmp/env", "w") do |f|
80
+ f.puts 'FOO=bar'
81
+ f.puts 'BAZ="qux"'
82
+ f.puts "FRED='barney'"
83
+ f.puts 'OTHER="escaped\"quote"'
84
+ end
85
+ engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
86
+ engine.environment.should == { "FOO" => "bar", "BAZ" => "qux", "FRED" => "barney", "OTHER" => 'escaped"quote' }
82
87
  end
83
88
 
84
89
  it "should fail if specified and doesnt exist" do
@@ -89,11 +94,27 @@ describe "Foreman::Engine" do
89
94
  it "should read .env if none specified" do
90
95
  File.open(".env", "w") { |f| f.puts("FOO=qoo") }
91
96
  engine = Foreman::Engine.new("Procfile")
92
- stub(engine).info
93
- mock(engine).watch_for_termination
94
- mock(engine).fork_individual(anything, anything, anything)
95
97
  engine.environment.should == {"FOO"=>"qoo"}
96
- engine.execute("bravo")
98
+ engine.start
99
+ end
100
+ end
101
+
102
+ describe "utf8" do
103
+ before(:each) do
104
+ File.open("Procfile", "w") do |file|
105
+ file.puts "utf8: #{resource_path("bin/utf8")}"
106
+ end
107
+ end
108
+
109
+ it "should spawn" do
110
+ stub(subject).watch_for_output
111
+ stub(subject).watch_for_termination
112
+ subject.start
113
+ Process.waitall
114
+ mock(subject).info(/started with pid \d+/, "utf8.1", anything)
115
+ mock(subject).info("\xff\x03\n", "utf8.1", anything)
116
+ subject.send(:poll_readers)
117
+ subject.send(:poll_readers)
97
118
  end
98
119
  end
99
120
  end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+ require "foreman/export/base"
3
+
4
+ describe "Foreman::Export::Base" do
5
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
6
+ let(:location) { "/tmp/init" }
7
+ let(:engine) { Foreman::Engine.new(procfile) }
8
+ let(:subject) { Foreman::Export::Base.new(location, engine) }
9
+
10
+ it "has a say method for displaying info" do
11
+ mock(subject).puts("[foreman export] foo")
12
+ subject.send(:say, "foo")
13
+ end
14
+
15
+ it "export needs to be overridden" do
16
+ lambda { subject.export }.should raise_error("export method must be overridden")
17
+ end
18
+
19
+ it "raises errors as a Foreman::Export::Exception" do
20
+ lambda { subject.send(:error, "foo") }.should raise_error(Foreman::Export::Exception, "foo")
21
+ end
22
+ end
@@ -3,18 +3,34 @@ require "foreman/engine"
3
3
  require "foreman/export/bluepill"
4
4
  require "tmpdir"
5
5
 
6
- describe Foreman::Export::Bluepill do
6
+ describe Foreman::Export::Bluepill, :fakefs do
7
7
  let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
8
- let(:engine) { Foreman::Engine.new(procfile) }
9
- let(:bluepill) { Foreman::Export::Bluepill.new(engine) }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:options) { Hash.new }
10
+ let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
10
11
 
11
12
  before(:each) { load_export_templates_into_fakefs("bluepill") }
12
13
  before(:each) { stub(bluepill).say }
13
14
 
14
15
  it "exports to the filesystem" do
15
- bluepill.export("/tmp/init", :concurrency => "alpha=2")
16
-
17
- File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
16
+ bluepill.export
17
+ normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
18
18
  end
19
19
 
20
- end
20
+ it "cleans up if exporting into an existing dir" do
21
+ mock(FileUtils).rm("/tmp/init/app.pill")
22
+
23
+ bluepill.export
24
+ bluepill.export
25
+ end
26
+
27
+ context "with concurrency" do
28
+ let(:options) { Hash[:concurrency => "alpha=2"] }
29
+
30
+ it "exports to the filesystem with concurrency" do
31
+ bluepill.export
32
+ normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/inittab"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Inittab, :fakefs do
7
+ let(:location) { "/tmp/inittab" }
8
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
9
+ let(:location) { "/tmp/inittab" }
10
+ let(:engine) { Foreman::Engine.new(procfile) }
11
+ let(:options) { Hash.new }
12
+ let(:inittab) { Foreman::Export::Inittab.new(location, engine, options) }
13
+
14
+ before(:each) { load_export_templates_into_fakefs("inittab") }
15
+ before(:each) { stub(inittab).say }
16
+
17
+ it "exports to the filesystem" do
18
+ inittab.export
19
+ File.read("/tmp/inittab").should == example_export_file("inittab/inittab.default")
20
+ end
21
+
22
+ context "to stdout" do
23
+ let(:location) { "-" }
24
+
25
+ it "exports to stdout" do
26
+ mock(inittab).puts example_export_file("inittab/inittab.default")
27
+ inittab.export
28
+ end
29
+ end
30
+
31
+ context "with concurrency" do
32
+ let(:options) { Hash[:concurrency => "alpha=2"] }
33
+
34
+ it "exports to the filesystem with concurrency" do
35
+ inittab.export
36
+ File.read("/tmp/inittab").should == example_export_file("inittab/inittab.concurrency")
37
+ end
38
+ end
39
+
40
+ end
@@ -3,33 +3,39 @@ require "foreman/engine"
3
3
  require "foreman/export/runit"
4
4
  require "tmpdir"
5
5
 
6
- describe Foreman::Export::Runit do
6
+ describe Foreman::Export::Runit, :fakefs do
7
7
  let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
8
8
  let(:engine) { Foreman::Engine.new(procfile) }
9
- let(:runit) { Foreman::Export::Runit.new(engine) }
10
-
9
+ let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, :concurrency => 'alpha=2,bravo=1') }
10
+
11
11
  before(:each) { load_export_templates_into_fakefs("runit") }
12
12
  before(:each) { stub(runit).say }
13
-
13
+ before(:each) { stub(FakeFS::FileUtils).chmod }
14
+
14
15
  it "exports to the filesystem" do
15
16
  FileUtils.mkdir_p('/tmp/init')
16
- runit.export('/tmp/init', :concurrency => 'alpha=2')
17
-
17
+
18
+ runit.export
19
+
18
20
  File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
19
- File.read("/tmp/init/app-alpha-1/log/run").should ==
21
+ File.read("/tmp/init/app-alpha-1/log/run").should ==
20
22
  example_export_file('runit/app-alpha-1-log-run')
21
23
  File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
22
24
  File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
23
-
25
+
24
26
  File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
25
- File.read("/tmp/init/app-alpha-2/log/run").should ==
27
+ File.read("/tmp/init/app-alpha-2/log/run").should ==
26
28
  example_export_file('runit/app-alpha-2-log-run')
27
29
  File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
28
30
  File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
29
-
31
+
30
32
  File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
31
- File.read("/tmp/init/app-bravo-1/log/run").should ==
33
+ File.read("/tmp/init/app-bravo-1/log/run").should ==
32
34
  example_export_file('runit/app-bravo-1-log-run')
33
35
  File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
34
36
  end
35
- end
37
+
38
+ it "creates a full path to the export directory" do
39
+ expect { runit.export }.to_not raise_error(Errno::ENOENT)
40
+ end
41
+ end
@@ -3,27 +3,53 @@ require "foreman/engine"
3
3
  require "foreman/export/upstart"
4
4
  require "tmpdir"
5
5
 
6
- describe Foreman::Export::Upstart do
6
+ describe Foreman::Export::Upstart, :fakefs do
7
7
  let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
8
- let(:engine) { Foreman::Engine.new(procfile) }
9
- let(:upstart) { Foreman::Export::Upstart.new(engine) }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:options) { Hash.new }
10
+ let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
10
11
 
11
12
  before(:each) { load_export_templates_into_fakefs("upstart") }
12
13
  before(:each) { stub(upstart).say }
13
14
 
14
15
  it "exports to the filesystem" do
15
- upstart.export("/tmp/init", :concurrency => "alpha=2")
16
+ upstart.export
16
17
 
17
18
  File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
18
19
  File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
19
20
  File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
20
- File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
21
21
  File.read("/tmp/init/app-bravo.conf").should == example_export_file("upstart/app-bravo.conf")
22
22
  File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
23
23
  end
24
24
 
25
+ it "cleans up if exporting into an existing dir" do
26
+ mock(FileUtils).rm("/tmp/init/app.conf")
27
+ mock(FileUtils).rm("/tmp/init/app-alpha.conf")
28
+ mock(FileUtils).rm("/tmp/init/app-alpha-1.conf")
29
+ mock(FileUtils).rm("/tmp/init/app-bravo.conf")
30
+ mock(FileUtils).rm("/tmp/init/app-bravo-1.conf")
31
+
32
+ upstart.export
33
+ upstart.export
34
+ end
35
+
36
+ context "with concurrency" do
37
+ let(:options) { Hash[:concurrency => "alpha=2"] }
38
+
39
+ it "exports to the filesystem with concurrency" do
40
+ upstart.export
41
+
42
+ File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
43
+ File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
44
+ File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
45
+ File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
46
+ File.exists?("/tmp/init/app-bravo-1.conf").should == false
47
+ end
48
+ end
49
+
25
50
  context "with alternate templates" do
26
51
  let(:template_root) { "/tmp/alternate" }
52
+ let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
27
53
 
28
54
  before do
29
55
  FileUtils.mkdir_p template_root
@@ -31,22 +57,28 @@ describe Foreman::Export::Upstart do
31
57
  end
32
58
 
33
59
  it "can export with alternate template files" do
34
- upstart.export("/tmp/init", :template => template_root)
60
+ upstart.export
35
61
 
36
62
  File.read("/tmp/init/app.conf").should == "alternate_template\n"
37
63
  end
38
64
  end
39
65
 
40
66
  context "with alternate templates from home dir" do
41
- let(:default_template_root) {File.expand_path("~/.foreman/templates")}
67
+ let(:default_template_root) {File.expand_path("#{ENV['HOME']}/.foreman/templates")}
42
68
 
43
69
  before do
70
+ ENV['_FOREMAN_SPEC_HOME'] = ENV['HOME']
71
+ ENV['HOME'] = "/home/appuser"
44
72
  FileUtils.mkdir_p default_template_root
45
73
  File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
46
74
  end
47
75
 
76
+ after do
77
+ ENV['HOME'] = ENV.delete('_FOREMAN_SPEC_HOME')
78
+ end
79
+
48
80
  it "can export with alternate template files" do
49
- upstart.export("/tmp/init")
81
+ upstart.export
50
82
 
51
83
  File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
52
84
  end
@@ -1,2 +1,24 @@
1
1
  require "spec_helper"
2
2
  require "foreman/export"
3
+
4
+ describe "Foreman::Export" do
5
+ subject { Foreman::Export }
6
+
7
+ describe "with a formatter that doesn't declare the appropriate class" do
8
+ it "prints an error" do
9
+ mock(subject).require("foreman/export/invalidformatter")
10
+ mock_export_error("Unknown export format: invalidformatter (no class Foreman::Export::Invalidformatter).") do
11
+ subject.formatter("invalidformatter")
12
+ end
13
+ end
14
+ end
15
+
16
+ describe "with an invalid formatter" do
17
+
18
+ it "prints an error" do
19
+ mock_export_error("Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
20
+ subject.formatter("invalidformatter")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+ require "foreman/helpers"
3
+
4
+ describe "Foreman::Helpers" do
5
+ before do
6
+ module Foo
7
+ class Bar; end
8
+ end
9
+ end
10
+
11
+ after do
12
+ Object.send(:remove_const, :Foo)
13
+ end
14
+
15
+ subject { o = Object.new; o.extend(Foreman::Helpers); o }
16
+
17
+ it "should classify words" do
18
+ subject.classify("foo").should == "Foo"
19
+ subject.classify("foo-bar").should == "FooBar"
20
+ end
21
+
22
+ it "should constantize words" do
23
+ subject.constantize("Object").should == Object
24
+ subject.constantize("Foo::Bar").should == Foo::Bar
25
+ end
26
+ end
@@ -1,2 +1,131 @@
1
- require "spec_helper"
2
- require "foreman/process"
1
+ require 'spec_helper'
2
+ require 'foreman/process'
3
+ require 'ostruct'
4
+ require 'timeout'
5
+ require 'tmpdir'
6
+
7
+ describe Foreman::Process do
8
+ subject { described_class.new entry, number, port }
9
+
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 }
27
+
28
+ after { FileUtils.remove_entry_secure basedir }
29
+
30
+ def run(cmd=command)
31
+ entry.command = cmd
32
+ subject.run pipe, basedir, env
33
+ subject.detach && sleep(init_delta)
34
+ end
35
+
36
+ def run_file(executable, code)
37
+ file = File.open("#{basedir}/script", 'w') {|it| it << code }
38
+ run "#{executable} #{file.path}"
39
+ sleep 1
40
+ end
41
+
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
62
+ end
63
+
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 arguments' do
125
+ pending
126
+ run %{ sh -c "trap '' TERM; sleep 10" }
127
+ subject.should be_alive
128
+ end
129
+ end
130
+ end
131
+ end
data/spec/foreman_spec.rb CHANGED
@@ -8,4 +8,9 @@ describe Foreman do
8
8
  it { should be_a String }
9
9
  end
10
10
 
11
+ describe "runner" do
12
+ it "should exist" do
13
+ File.exists?(Foreman.runner).should == true
14
+ end
15
+ end
11
16
  end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+ describe "spec helpers" do
4
+ describe "#preserving_env" do
5
+ after { ENV.delete "FOO" }
6
+
7
+ it "should remove added environment vars" do
8
+ preserving_env { ENV["FOO"] = "baz" }
9
+ ENV["FOO"].should == nil
10
+ end
11
+
12
+ it "should reset modified environment vars" do
13
+ ENV["FOO"] = "bar"
14
+ preserving_env { ENV["FOO"] = "baz"}
15
+ ENV["FOO"].should == "bar"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ puts "\xff\x03"
@@ -0,0 +1,47 @@
1
+ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
2
+
3
+ app.uid = "app"
4
+ app.gid = "app"
5
+
6
+
7
+
8
+
9
+ app.process("alpha-1") do |process|
10
+ process.start_command = "./alpha"
11
+
12
+ process.working_dir = "/tmp/app"
13
+ process.daemonize = true
14
+ process.environment = {"PORT" => "5000"}
15
+ process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
+
17
+ process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
18
+
19
+ process.monitor_children do |children|
20
+ children.stop_command "kill -QUIT {{PID}}"
21
+ end
22
+
23
+ process.group = "app-alpha"
24
+ end
25
+
26
+
27
+ app.process("alpha-2") do |process|
28
+ process.start_command = "./alpha"
29
+
30
+ process.working_dir = "/tmp/app"
31
+ process.daemonize = true
32
+ process.environment = {"PORT" => "5001"}
33
+ process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
34
+
35
+ process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
36
+
37
+ process.monitor_children do |children|
38
+ children.stop_command "kill -QUIT {{PID}}"
39
+ end
40
+
41
+ process.group = "app-alpha"
42
+ end
43
+
44
+
45
+
46
+
47
+ end