foreman 0.37.0-mingw32

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 (61) hide show
  1. data/README.md +39 -0
  2. data/bin/foreman +7 -0
  3. data/bin/runner +36 -0
  4. data/data/example/Procfile +2 -0
  5. data/data/example/Procfile.without_colon +2 -0
  6. data/data/example/error +7 -0
  7. data/data/example/log/neverdie.log +4 -0
  8. data/data/example/ticker +14 -0
  9. data/data/export/bluepill/master.pill.erb +27 -0
  10. data/data/export/runit/log_run.erb +7 -0
  11. data/data/export/runit/run.erb +3 -0
  12. data/data/export/upstart/master.conf.erb +8 -0
  13. data/data/export/upstart/process.conf.erb +5 -0
  14. data/data/export/upstart/process_master.conf.erb +2 -0
  15. data/lib/foreman.rb +25 -0
  16. data/lib/foreman/cli.rb +98 -0
  17. data/lib/foreman/distribution.rb +9 -0
  18. data/lib/foreman/engine.rb +234 -0
  19. data/lib/foreman/export.rb +32 -0
  20. data/lib/foreman/export/base.rb +51 -0
  21. data/lib/foreman/export/bluepill.rb +26 -0
  22. data/lib/foreman/export/inittab.rb +36 -0
  23. data/lib/foreman/export/runit.rb +59 -0
  24. data/lib/foreman/export/upstart.rb +41 -0
  25. data/lib/foreman/helpers.rb +45 -0
  26. data/lib/foreman/process.rb +96 -0
  27. data/lib/foreman/procfile.rb +38 -0
  28. data/lib/foreman/procfile_entry.rb +22 -0
  29. data/lib/foreman/utils.rb +18 -0
  30. data/lib/foreman/version.rb +5 -0
  31. data/man/foreman.1 +222 -0
  32. data/spec/foreman/cli_spec.rb +163 -0
  33. data/spec/foreman/engine_spec.rb +86 -0
  34. data/spec/foreman/export/base_spec.rb +22 -0
  35. data/spec/foreman/export/bluepill_spec.rb +36 -0
  36. data/spec/foreman/export/inittab_spec.rb +40 -0
  37. data/spec/foreman/export/runit_spec.rb +41 -0
  38. data/spec/foreman/export/upstart_spec.rb +87 -0
  39. data/spec/foreman/export_spec.rb +24 -0
  40. data/spec/foreman/helpers_spec.rb +26 -0
  41. data/spec/foreman/process_spec.rb +131 -0
  42. data/spec/foreman_spec.rb +34 -0
  43. data/spec/helper_spec.rb +18 -0
  44. data/spec/resources/export/bluepill/app-concurrency.pill +47 -0
  45. data/spec/resources/export/bluepill/app.pill +44 -0
  46. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  47. data/spec/resources/export/inittab/inittab.default +4 -0
  48. data/spec/resources/export/runit/app-alpha-1-log-run +7 -0
  49. data/spec/resources/export/runit/app-alpha-1-run +3 -0
  50. data/spec/resources/export/runit/app-alpha-2-log-run +7 -0
  51. data/spec/resources/export/runit/app-alpha-2-run +3 -0
  52. data/spec/resources/export/runit/app-bravo-1-log-run +7 -0
  53. data/spec/resources/export/runit/app-bravo-1-run +3 -0
  54. data/spec/resources/export/upstart/app-alpha-1.conf +5 -0
  55. data/spec/resources/export/upstart/app-alpha-2.conf +5 -0
  56. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  57. data/spec/resources/export/upstart/app-bravo-1.conf +5 -0
  58. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  59. data/spec/resources/export/upstart/app.conf +8 -0
  60. data/spec/spec_helper.rb +98 -0
  61. metadata +138 -0
@@ -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
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/bluepill"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Bluepill, :fakefs do
7
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:options) { Hash.new }
10
+ let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
11
+
12
+ before(:each) { load_export_templates_into_fakefs("bluepill") }
13
+ before(:each) { stub(bluepill).say }
14
+
15
+ it "exports to the filesystem" do
16
+ bluepill.export
17
+ normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
18
+ end
19
+
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
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/runit"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Runit, :fakefs do
7
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, :concurrency => 'alpha=2,bravo=1') }
10
+
11
+ before(:each) { load_export_templates_into_fakefs("runit") }
12
+ before(:each) { stub(runit).say }
13
+ before(:each) { stub(FakeFS::FileUtils).chmod }
14
+
15
+ it "exports to the filesystem" do
16
+ FileUtils.mkdir_p('/tmp/init')
17
+
18
+ runit.export
19
+
20
+ File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
21
+ File.read("/tmp/init/app-alpha-1/log/run").should ==
22
+ example_export_file('runit/app-alpha-1-log-run')
23
+ File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
24
+ File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
25
+
26
+ File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
27
+ File.read("/tmp/init/app-alpha-2/log/run").should ==
28
+ example_export_file('runit/app-alpha-2-log-run')
29
+ File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
30
+ File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
31
+
32
+ File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
33
+ File.read("/tmp/init/app-bravo-1/log/run").should ==
34
+ example_export_file('runit/app-bravo-1-log-run')
35
+ File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
36
+ 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
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/upstart"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Upstart, :fakefs do
7
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:options) { Hash.new }
10
+ let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
11
+
12
+ before(:each) { load_export_templates_into_fakefs("upstart") }
13
+ before(:each) { stub(upstart).say }
14
+
15
+ it "exports to the filesystem" do
16
+ upstart.export
17
+
18
+ File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
19
+ File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
20
+ File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
21
+ File.read("/tmp/init/app-bravo.conf").should == example_export_file("upstart/app-bravo.conf")
22
+ File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
23
+ end
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
+
50
+ context "with alternate templates" do
51
+ let(:template_root) { "/tmp/alternate" }
52
+ let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
53
+
54
+ before do
55
+ FileUtils.mkdir_p template_root
56
+ File.open("#{template_root}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
57
+ end
58
+
59
+ it "can export with alternate template files" do
60
+ upstart.export
61
+
62
+ File.read("/tmp/init/app.conf").should == "alternate_template\n"
63
+ end
64
+ end
65
+
66
+ context "with alternate templates from home dir" do
67
+ let(:default_template_root) {File.expand_path("#{ENV['HOME']}/.foreman/templates")}
68
+
69
+ before do
70
+ ENV['_FOREMAN_SPEC_HOME'] = ENV['HOME']
71
+ ENV['HOME'] = "/home/appuser"
72
+ FileUtils.mkdir_p default_template_root
73
+ File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
74
+ end
75
+
76
+ after do
77
+ ENV['HOME'] = ENV.delete('_FOREMAN_SPEC_HOME')
78
+ end
79
+
80
+ it "can export with alternate template files" do
81
+ upstart.export
82
+
83
+ File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
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
@@ -0,0 +1,131 @@
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
+ end
40
+
41
+ context 'options' do
42
+ it 'should set PORT for environment' do
43
+ mock(subject).run_process(basedir, command, pipe) do
44
+ ENV['PORT'].should == port.to_s
45
+ end
46
+ run
47
+ end
48
+
49
+ it 'should set custom variables for environment' do
50
+ mock(subject).run_process(basedir, command, pipe) do
51
+ ENV['foo'].should == 'bar'
52
+ end
53
+ run
54
+ end
55
+
56
+ it 'should restore environment afterwards' do
57
+ mock(subject).run_process(basedir, command, pipe)
58
+ run
59
+ ENV.should_not include('PORT', 'foo')
60
+ end
61
+ end
62
+
63
+ context 'process' do
64
+ around do |spec|
65
+ IO.pipe do |reader, writer|
66
+ @reader, @writer = reader, writer
67
+ spec.run
68
+ end
69
+ end
70
+
71
+ let(:pipe) { @writer }
72
+ let(:output) { @reader.read_nonblock 1024 }
73
+
74
+ it 'should not block' do
75
+ expect {
76
+ Timeout.timeout(2*init_delta) { run 'sleep 2' }
77
+ }.should_not raise_exception
78
+ end
79
+
80
+ it 'should be alive' do
81
+ run 'sleep 1'
82
+ subject.should be_alive
83
+ end
84
+
85
+ it 'should be dead' do
86
+ run 'exit'
87
+ subject.should be_dead
88
+ end
89
+
90
+ it 'should be killable' do
91
+ run 'sleep 1'
92
+ subject.kill 'TERM'
93
+ subject.should be_dead
94
+ end
95
+
96
+ it 'should send different signals' do
97
+ run_file 'ruby', <<-CODE
98
+ trap "TERM", "IGNORE"
99
+ loop { sleep 1 }
100
+ CODE
101
+ sleep 1 # wait for ruby to start
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
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "foreman"
3
+
4
+ describe Foreman do
5
+
6
+ describe "VERSION" do
7
+ subject { Foreman::VERSION }
8
+ it { should be_a String }
9
+ end
10
+
11
+ describe "::load_env!(env_file)", :fakefs do
12
+ after do
13
+ ENV['FOO'] = nil
14
+ end
15
+
16
+ it "should load env_file into ENV" do
17
+ File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
18
+ Foreman.load_env!("/tmp/env1")
19
+ ENV['FOO'].should == 'bar'
20
+ end
21
+
22
+ it "should assume env_file in ./.env" do
23
+ File.open("./.env", "w") { |f| f.puts("FOO=bar") }
24
+ Foreman.load_env!
25
+ ENV['FOO'].should == 'bar'
26
+ end
27
+ end
28
+
29
+ describe "runner" do
30
+ it "should exist" do
31
+ File.exists?(Foreman.runner).should == true
32
+ end
33
+ end
34
+ end