foreman 0.31.0 → 0.50.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 (73) hide show
  1. data/README.md +46 -0
  2. data/bin/foreman-runner +32 -0
  3. data/bin/taskman +8 -0
  4. data/data/example/Procfile +4 -2
  5. data/data/example/spawnee +14 -0
  6. data/data/example/spawner +7 -0
  7. data/data/example/utf8 +11 -0
  8. data/data/export/bluepill/master.pill.erb +11 -10
  9. data/data/export/launchd/launchd.plist.erb +22 -0
  10. data/data/export/runit/log/run.erb +7 -0
  11. data/data/export/runit/run.erb +2 -2
  12. data/data/export/supervisord/app.conf.erb +27 -0
  13. data/data/export/upstart/master.conf.erb +2 -2
  14. data/data/export/upstart/process.conf.erb +3 -3
  15. data/lib/foreman/cli.rb +83 -34
  16. data/lib/foreman/engine/cli.rb +105 -0
  17. data/lib/foreman/engine.rb +232 -140
  18. data/lib/foreman/env.rb +27 -0
  19. data/lib/foreman/export/base.rb +108 -6
  20. data/lib/foreman/export/bluepill.rb +4 -20
  21. data/lib/foreman/export/inittab.rb +11 -16
  22. data/lib/foreman/export/launchd.rb +15 -0
  23. data/lib/foreman/export/runit.rb +15 -41
  24. data/lib/foreman/export/supervisord.rb +16 -0
  25. data/lib/foreman/export/upstart.rb +10 -28
  26. data/lib/foreman/export.rb +23 -0
  27. data/lib/foreman/helpers.rb +45 -0
  28. data/lib/foreman/process.rb +80 -46
  29. data/lib/foreman/procfile.rb +68 -14
  30. data/lib/foreman/version.rb +1 -1
  31. data/lib/foreman.rb +12 -7
  32. data/man/foreman.1 +29 -3
  33. data/spec/foreman/cli_spec.rb +54 -60
  34. data/spec/foreman/engine_spec.rb +64 -46
  35. data/spec/foreman/export/base_spec.rb +19 -0
  36. data/spec/foreman/export/bluepill_spec.rb +24 -6
  37. data/spec/foreman/export/inittab_spec.rb +40 -0
  38. data/spec/foreman/export/launchd_spec.rb +21 -0
  39. data/spec/foreman/export/runit_spec.rb +23 -22
  40. data/spec/foreman/export/supervisord_spec.rb +36 -0
  41. data/spec/foreman/export/upstart_spec.rb +49 -16
  42. data/spec/foreman/export_spec.rb +22 -0
  43. data/spec/foreman/helpers_spec.rb +26 -0
  44. data/spec/foreman/process_spec.rb +48 -2
  45. data/spec/foreman/procfile_spec.rb +41 -0
  46. data/spec/foreman_spec.rb +3 -20
  47. data/spec/helper_spec.rb +18 -0
  48. data/spec/resources/Procfile +4 -0
  49. data/spec/resources/bin/echo +2 -0
  50. data/spec/resources/bin/env +2 -0
  51. data/spec/resources/bin/test +2 -0
  52. data/spec/resources/bin/utf8 +2 -0
  53. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  54. data/spec/resources/export/bluepill/app.pill +6 -25
  55. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  56. data/spec/resources/export/inittab/inittab.default +4 -0
  57. data/spec/resources/export/launchd/launchd-a.default +22 -0
  58. data/spec/resources/export/launchd/launchd-b.default +22 -0
  59. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  60. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  61. data/spec/spec_helper.rb +93 -10
  62. metadata +72 -76
  63. data/README.markdown +0 -49
  64. data/bin/runner +0 -2
  65. data/data/export/runit/log_run.erb +0 -7
  66. data/lib/foreman/procfile_entry.rb +0 -22
  67. data/lib/foreman/utils.rb +0 -15
  68. /data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
  69. /data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
  70. /data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
  71. /data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
  72. /data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
  73. /data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
@@ -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,48 @@
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
+
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
14
+
15
+ describe "#run" do
16
+
17
+ it "runs the process" do
18
+ process = Foreman::Process.new(resource_path("bin/test"))
19
+ run(process).should == "testing\n"
20
+ end
21
+
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"
25
+ end
26
+
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"
30
+ end
31
+
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
36
+
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
41
+
42
+ it "should output utf8 properly" do
43
+ process = Foreman::Process.new(resource_path("bin/utf8"))
44
+ run(process).should == "\xFF\x03\n"
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'foreman/procfile'
3
+ require 'pathname'
4
+ require 'tmpdir'
5
+
6
+ describe Foreman::Procfile, :fakefs do
7
+ subject { Foreman::Procfile.new }
8
+
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
22
+
23
+ it "can have a process appended to it" do
24
+ subject["charlie"] = "./charlie"
25
+ subject["charlie"].should == "./charlie"
26
+ end
27
+
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"
32
+ end
33
+
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"
39
+ end
40
+
41
+ end
data/spec/foreman_spec.rb CHANGED
@@ -8,26 +8,9 @@ describe Foreman do
8
8
  it { should be_a String }
9
9
  end
10
10
 
11
- describe "::load_env!(env_file)" do
12
- before do
13
- FakeFS.activate!
14
- end
15
-
16
- after do
17
- FakeFS.deactivate!
18
- ENV['FOO'] = nil
19
- end
20
-
21
- it "should load env_file into ENV" do
22
- File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
23
- Foreman.load_env!("/tmp/env1")
24
- ENV['FOO'].should == 'bar'
25
- end
26
-
27
- it "should assume env_file in ./.env" do
28
- File.open("./.env", "w") { |f| f.puts("FOO=bar") }
29
- Foreman.load_env!
30
- ENV['FOO'].should == 'bar'
11
+ describe "runner" do
12
+ it "should exist" do
13
+ File.exists?(Foreman.runner).should == true
31
14
  end
32
15
  end
33
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,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"
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ puts "\xff\x03"
@@ -0,0 +1,49 @@
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
+ process.stop_grace_time = 45.seconds
17
+
18
+ process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
19
+
20
+ process.monitor_children do |children|
21
+ children.stop_command "kill {{PID}}"
22
+ end
23
+
24
+ process.group = "app-alpha"
25
+ end
26
+
27
+
28
+ app.process("alpha-2") do |process|
29
+ process.start_command = "./alpha"
30
+
31
+ process.working_dir = "/tmp/app"
32
+ process.daemonize = true
33
+ process.environment = {"PORT"=>"5001"}
34
+ process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
35
+ process.stop_grace_time = 45.seconds
36
+
37
+ process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
38
+
39
+ process.monitor_children do |children|
40
+ children.stop_command "kill {{PID}}"
41
+ end
42
+
43
+ process.group = "app-alpha"
44
+ end
45
+
46
+
47
+
48
+
49
+ end
@@ -11,51 +11,32 @@ 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
+ process.stop_grace_time = 45.seconds
16
17
 
17
18
  process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
18
19
 
19
20
  process.monitor_children do |children|
20
- children.stop_command "kill -QUIT {{PID}}"
21
+ children.stop_command "kill {{PID}}"
21
22
  end
22
23
 
23
24
  process.group = "app-alpha"
24
25
  end
25
26
 
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
27
  app.process("bravo-1") do |process|
48
28
  process.start_command = "./bravo"
49
29
 
50
30
  process.working_dir = "/tmp/app"
51
31
  process.daemonize = true
52
- process.environment = {"PORT" => "5100"}
32
+ process.environment = {"PORT"=>"5100"}
53
33
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
34
+ process.stop_grace_time = 45.seconds
54
35
 
55
36
  process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
56
37
 
57
38
  process.monitor_children do |children|
58
- children.stop_command "kill -QUIT {{PID}}"
39
+ children.stop_command "kill {{PID}}"
59
40
  end
60
41
 
61
42
  process.group = "app-bravo"
@@ -0,0 +1,4 @@
1
+ # ----- foreman app processes -----
2
+ AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
3
+ AP02:4:respawn:/bin/su - app -c 'PORT=5001 ./alpha >> /var/log/app/alpha-2.log 2>&1'
4
+ # ----- end foreman app processes -----
@@ -0,0 +1,4 @@
1
+ # ----- foreman app processes -----
2
+ AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
3
+ AP02:4:respawn:/bin/su - app -c 'PORT=5100 ./bravo >> /var/log/app/bravo-1.log 2>&1'
4
+ # ----- end foreman app processes -----
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>app-alpha-1</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>./alpha</string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string>/var/log/app/app-alpha-1.log</string>
17
+ <key>UserName</key>
18
+ <string>app</string>
19
+ <key>WorkingDirectory</key>
20
+ <string>/tmp/app</string>
21
+ </dict>
22
+ </plist>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>app-bravo-1</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>./bravo</string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string>/var/log/app/app-bravo-1.log</string>
17
+ <key>UserName</key>
18
+ <string>app</string>
19
+ <key>WorkingDirectory</key>
20
+ <string>/tmp/app</string>
21
+ </dict>
22
+ </plist>
@@ -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
@@ -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-alpha-2]
13
+ command=./alpha
14
+ autostart=true
15
+ autorestart=true
16
+ stopsignal=QUIT
17
+ stdout_logfile=/var/log/app/alpha-2.log
18
+ stderr_logfile=/var/log/app/alpha-2.error.log
19
+ user=app
20
+ directory=/tmp/app
21
+ environment=PORT="5001"
22
+
23
+ [group:app]
24
+ programs=app-alpha-1,app-alpha-2
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,21 @@
1
1
  require "rubygems"
2
+
3
+ require "simplecov"
4
+ SimpleCov.start do
5
+ add_filter "/spec/"
6
+ end
7
+
2
8
  require "rspec"
9
+ require "timecop"
3
10
  require "fakefs/safe"
4
11
  require "fakefs/spec_helpers"
5
12
 
6
13
  $:.unshift File.expand_path("../../lib", __FILE__)
7
14
 
15
+ def mock_export_error(message)
16
+ lambda { yield }.should raise_error(Foreman::Export::Exception, message)
17
+ end
18
+
8
19
  def mock_error(subject, message)
9
20
  mock_exit do
10
21
  mock(subject).puts("ERROR: #{message}")
@@ -13,7 +24,38 @@ def mock_error(subject, message)
13
24
  end
14
25
 
15
26
  def foreman(args)
16
- 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
+ buffer += rd.gets
58
+ end
17
59
  end
18
60
 
19
61
  def mock_exit(&block)
@@ -37,34 +79,75 @@ def write_procfile(procfile="Procfile", alpha_env="")
37
79
  File.expand_path(procfile)
38
80
  end
39
81
 
40
- def write_env(env=".env")
82
+ def write_env(env=".env", options={"FOO"=>"bar"})
41
83
  File.open(env, "w") do |file|
42
- file.puts "FOO=bar"
84
+ options.each do |key, val|
85
+ file.puts "#{key}=#{val}"
86
+ end
43
87
  end
44
88
  end
45
89
 
46
- def load_export_templates_into_fakefs(type)
90
+ def without_fakefs
47
91
  FakeFS.deactivate!
48
- files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
49
- hash.update(file => File.read(file))
50
- end
92
+ ret = yield
51
93
  FakeFS.activate!
52
- files.each do |filename, contents|
94
+ ret
95
+ end
96
+
97
+ def load_export_templates_into_fakefs(type)
98
+ without_fakefs do
99
+ Dir[File.expand_path("../../data/export/#{type}/**/*", __FILE__)].inject({}) do |hash, file|
100
+ next(hash) if File.directory?(file)
101
+ hash.update(file => File.read(file))
102
+ end
103
+ end.each do |filename, contents|
104
+ FileUtils.mkdir_p File.dirname(filename)
53
105
  File.open(filename, "w") do |f|
54
106
  f.puts contents
55
107
  end
56
108
  end
57
109
  end
58
110
 
111
+ def resource_path(filename)
112
+ File.expand_path("../resources/#{filename}", __FILE__)
113
+ end
114
+
59
115
  def example_export_file(filename)
60
116
  FakeFS.deactivate!
61
- data = File.read(File.expand_path("../resources/export/#{filename}", __FILE__))
117
+ data = File.read(File.expand_path(resource_path("export/#{filename}"), __FILE__))
62
118
  FakeFS.activate!
63
119
  data
64
120
  end
65
121
 
122
+ def preserving_env
123
+ old_env = ENV.to_hash
124
+ begin
125
+ yield
126
+ ensure
127
+ ENV.clear
128
+ ENV.update(old_env)
129
+ end
130
+ end
131
+
132
+ def normalize_space(s)
133
+ s.gsub(/\n[\n\s]*/, "\n")
134
+ end
135
+
136
+ def capture_stdout
137
+ old_stdout = $stdout.dup
138
+ rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
139
+ $stdout = wr
140
+ yield
141
+ wr.close
142
+ rd.read
143
+ ensure
144
+ $stdout = old_stdout
145
+ end
146
+
66
147
  RSpec.configure do |config|
148
+ config.treat_symbols_as_metadata_keys_with_true_values = true
67
149
  config.color_enabled = true
68
- config.include FakeFS::SpecHelpers
150
+ config.order = 'rand'
151
+ config.include FakeFS::SpecHelpers, :fakefs
69
152
  config.mock_with :rr
70
153
  end