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.
- data/README.md +40 -0
- data/bin/foreman-runner +36 -0
- data/data/example/Procfile +1 -0
- data/data/example/utf8 +11 -0
- data/data/export/bluepill/master.pill.erb +3 -3
- data/lib/foreman/cli.rb +35 -31
- data/lib/foreman/engine.rb +109 -82
- data/lib/foreman/export/base.rb +12 -5
- data/lib/foreman/export/bluepill.rb +5 -7
- data/lib/foreman/export/inittab.rb +11 -13
- data/lib/foreman/export/runit.rb +24 -25
- data/lib/foreman/export/upstart.rb +9 -11
- data/lib/foreman/export.rb +21 -0
- data/lib/foreman/helpers.rb +45 -0
- data/lib/foreman/process.rb +88 -6
- data/lib/foreman/procfile.rb +8 -7
- data/lib/foreman/procfile_entry.rb +22 -0
- data/lib/foreman/utils.rb +4 -1
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +12 -1
- data/man/foreman.1 +17 -3
- data/spec/foreman/cli_spec.rb +96 -7
- data/spec/foreman/engine_spec.rb +52 -31
- data/spec/foreman/export/base_spec.rb +22 -0
- data/spec/foreman/export/bluepill_spec.rb +23 -7
- data/spec/foreman/export/inittab_spec.rb +40 -0
- data/spec/foreman/export/runit_spec.rb +18 -12
- data/spec/foreman/export/upstart_spec.rb +40 -8
- data/spec/foreman/export_spec.rb +22 -0
- data/spec/foreman/helpers_spec.rb +26 -0
- data/spec/foreman/process_spec.rb +131 -2
- data/spec/foreman_spec.rb +5 -0
- data/spec/helper_spec.rb +18 -0
- data/spec/resources/bin/utf8 +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +47 -0
- data/spec/resources/export/bluepill/app.pill +1 -22
- data/spec/resources/export/inittab/inittab.concurrency +4 -0
- data/spec/resources/export/inittab/inittab.default +4 -0
- data/spec/spec_helper.rb +36 -4
- metadata +23 -11
- data/README.markdown +0 -49
data/spec/foreman/engine_spec.rb
CHANGED
|
@@ -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(
|
|
28
|
-
mock(
|
|
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(
|
|
37
|
-
mock(
|
|
38
|
-
mock(engine).
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
9
|
-
let(:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
9
|
-
let(:
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
81
|
+
upstart.export
|
|
50
82
|
|
|
51
83
|
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
|
52
84
|
end
|
data/spec/foreman/export_spec.rb
CHANGED
|
@@ -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
|
|
2
|
-
require
|
|
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
data/spec/helper_spec.rb
ADDED
|
@@ -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,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
|