foreman 0.37.0.pre3-mingw32 → 0.37.0.pre4-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,49 +1,38 @@
1
1
  # Foreman
2
2
 
3
- ## Installation
3
+ Manage Procfile-based applications
4
4
 
5
- * Rubygems
5
+ <table>
6
+ <tr>
7
+ <th>If you have...</th>
8
+ <th>Install with...</th>
9
+ </tr>
10
+ <tr>
11
+ <td>Ruby (MRI, JRuby, Windows)</td>
12
+ <td><pre>$ gem install foreman</pre></td>
13
+ </tr>
14
+ <tr>
15
+ <td>Mac OS X</td>
16
+ <td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
17
+ </tr>
18
+ </table>
6
19
 
7
- gem install foreman
20
+ ## Getting Started
8
21
 
9
- * OSX
22
+ * http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
10
23
 
11
- http://assets.foreman.io/foreman/foreman.pkg
12
-
13
- * Standalone Tarball
14
-
15
- http://assets.foreman.io/foreman/foreman.tgz
16
-
17
- ## Description
18
-
19
- http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
20
-
21
- ## Manual
24
+ ## Documentation
22
25
 
23
26
  * [man page](http://ddollar.github.com/foreman)
24
27
  * [wiki](http://github.com/ddollar/foreman/wiki)
25
28
 
26
- ## Authorship
27
-
28
- Created by David Dollar
29
-
30
- Patches contributed by:
31
-
32
- * Adam Wiggins
33
- * Dan Peterson
34
- * Hunter Nield
35
- * Jay Zeschin
36
- * Keith Rarick
37
- * Khaja Minhajuddin
38
- * Matt Haynes
39
- * Michael van Rooijen
40
- * Mike Javorski
41
- * Nathan L Smith
42
- * Nick Zadrozny
43
- * Ricardo Chimal, Jr
44
- * Thom May
45
- * clifff
46
- * Greg Reinacker
29
+ ## Authors
30
+
31
+ #### Created and maintained by
32
+ David Dollar
33
+
34
+ #### Patches contributed by
35
+ Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
47
36
 
48
37
  ## License
49
38
 
@@ -5,7 +5,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
5
5
 
6
6
  <% engine.procfile.entries.each do |process| %>
7
7
  <% 1.upto(concurrency[process.name]) do |num| %>
8
- <% port = engine.port_for(process, num, options[:port]) %>
8
+ <% port = engine.port_for(process, num, self.port) %>
9
9
  app.process("<%= process.name %>-<%=num%>") do |process|
10
10
  process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
11
11
 
data/lib/foreman/cli.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  require "foreman"
2
+ require "foreman/helpers"
2
3
  require "foreman/engine"
3
4
  require "foreman/export"
4
5
  require "thor"
5
6
  require "yaml"
6
7
 
7
8
  class Foreman::CLI < Thor
9
+ include Foreman::Helpers
10
+
11
+ class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
8
12
 
9
13
  desc "start", "Start the application"
10
14
 
@@ -42,16 +46,17 @@ class Foreman::CLI < Thor
42
46
  def export(format, location=nil)
43
47
  check_procfile!
44
48
 
45
- formatter = case format
46
- when "inittab" then Foreman::Export::Inittab
47
- when "upstart" then Foreman::Export::Upstart
48
- when "bluepill" then Foreman::Export::Bluepill
49
- when "runit" then Foreman::Export::Runit
50
- else error "Unknown export format: #{format}."
49
+ begin
50
+ require "foreman/export/#{ format.tr('-', '_') }"
51
+ classy_format = classify(format)
52
+ formatter = constantize("Foreman::Export::#{ classy_format }")
53
+ rescue NameError => ex
54
+ error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
55
+ rescue LoadError => ex
56
+ error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
51
57
  end
52
58
 
53
- formatter.new(engine).export(location, options)
54
-
59
+ formatter.new(location, engine, options).export
55
60
  rescue Foreman::Export::Exception => ex
56
61
  error ex.message
57
62
  end
@@ -73,7 +73,7 @@ private ######################################################################
73
73
  def kill_all(signal="SIGTERM")
74
74
  running_processes.each do |pid, process|
75
75
  info "sending #{signal} to pid #{pid}"
76
- Process.kill(signal, pid) rescue Errno::ESRCH
76
+ process.kill signal
77
77
  end
78
78
  end
79
79
 
@@ -93,7 +93,9 @@ private ######################################################################
93
93
  loop do
94
94
  rs, ws = IO.select(readers.values, [], [], 1)
95
95
  (rs || []).each do |r|
96
- ps, message = r.gets.split(",", 2)
96
+ data = r.gets
97
+ next unless data
98
+ ps, message = data.split(",", 2)
97
99
  color = colors[ps.split(".").first]
98
100
  info message, ps, color
99
101
  end
@@ -134,11 +136,6 @@ private ######################################################################
134
136
  end
135
137
  end
136
138
 
137
- def error(message)
138
- puts "ERROR: #{message}"
139
- exit 1
140
- end
141
-
142
139
  def longest_process_name
143
140
  @longest_process_name ||= begin
144
141
  longest = procfile.process_names.map { |name| name.length }.sort.last
@@ -217,6 +214,11 @@ private ######################################################################
217
214
  def apply_environment!
218
215
  @environment.each { |k,v| ENV[k] = v }
219
216
  end
217
+
218
+ def error(message)
219
+ puts "ERROR: #{message}"
220
+ exit 1
221
+ end
220
222
  end
221
223
 
222
224
  include Env
@@ -3,10 +3,17 @@ require "foreman/utils"
3
3
 
4
4
  class Foreman::Export::Base
5
5
 
6
- attr_reader :engine
7
-
8
- def initialize(engine)
9
- @engine = engine
6
+ attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
7
+
8
+ def initialize(location, engine, options={})
9
+ @location = location
10
+ @engine = engine
11
+ @app = options[:app]
12
+ @log = options[:log]
13
+ @port = options[:port]
14
+ @user = options[:user]
15
+ @template = options[:template]
16
+ @concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
10
17
  end
11
18
 
12
19
  def export
@@ -3,23 +3,21 @@ require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Bluepill < Foreman::Export::Base
5
5
 
6
- def export(location, options={})
6
+ def export
7
7
  error("Must specify a location") unless location
8
8
 
9
9
  FileUtils.mkdir_p location
10
10
 
11
- app = options[:app] || File.basename(engine.directory)
12
- user = options[:user] || app
13
- log_root = options[:log] || "/var/log/#{app}"
14
- template_root = options[:template]
11
+ app = self.app || File.basename(engine.directory)
12
+ user = self.user || app
13
+ log_root = self.log || "/var/log/#{app}"
14
+ template_root = self.template
15
15
 
16
16
  Dir["#{location}/#{app}.pill"].each do |file|
17
17
  say "cleaning up: #{file}"
18
18
  FileUtils.rm(file)
19
19
  end
20
20
 
21
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
22
-
23
21
  master_template = export_template("bluepill", "master.pill.erb", template_root)
24
22
  master_config = ERB.new(master_template).result(binding)
25
23
  write_file "#{location}/#{app}.pill", master_config
@@ -2,20 +2,18 @@ require "foreman/export"
2
2
 
3
3
  class Foreman::Export::Inittab < Foreman::Export::Base
4
4
 
5
- def export(fname=nil, options={})
6
- app = options[:app] || File.basename(engine.directory)
7
- user = options[:user] || app
8
- log_root = options[:log] || "/var/log/#{app}"
9
-
10
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
5
+ def export
6
+ app = self.app || File.basename(engine.directory)
7
+ user = self.user || app
8
+ log_root = self.log || "/var/log/#{app}"
11
9
 
12
10
  inittab = []
13
11
  inittab << "# ----- foreman #{app} processes -----"
14
12
 
15
13
  engine.procfile.entries.inject(1) do |index, process|
16
- 1.upto(concurrency[process.name]) do |num|
14
+ 1.upto(self.concurrency[process.name]) do |num|
17
15
  id = app.slice(0, 2).upcase + sprintf("%02d", index)
18
- port = engine.port_for(process, num, options[:port])
16
+ port = engine.port_for(process, num, self.port)
19
17
  inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
20
18
  index += 1
21
19
  end
@@ -4,21 +4,19 @@ require "foreman/export"
4
4
  class Foreman::Export::Runit < Foreman::Export::Base
5
5
  ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
6
6
 
7
- def export(location, options={})
7
+ def export
8
8
  error("Must specify a location") unless location
9
9
 
10
- app = options[:app] || File.basename(engine.directory)
11
- user = options[:user] || app
12
- log_root = options[:log] || "/var/log/#{app}"
13
- template_root = options[:template]
14
-
15
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
10
+ app = self.app || File.basename(engine.directory)
11
+ user = self.user || app
12
+ log_root = self.log || "/var/log/#{app}"
13
+ template_root = self.template
16
14
 
17
15
  run_template = export_template('runit', 'run.erb', template_root)
18
16
  log_run_template = export_template('runit', 'log_run.erb', template_root)
19
17
 
20
18
  engine.procfile.entries.each do |process|
21
- 1.upto(concurrency[process.name]) do |num|
19
+ 1.upto(self.concurrency[process.name]) do |num|
22
20
  process_directory = "#{location}/#{app}-#{process.name}-#{num}"
23
21
  process_env_directory = "#{process_directory}/env"
24
22
  process_log_directory = "#{process_directory}/log"
@@ -31,7 +29,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
31
29
  write_file "#{process_directory}/run", run
32
30
  FileUtils.chmod 0755, "#{process_directory}/run"
33
31
 
34
- port = engine.port_for(process, num, options[:port])
32
+ port = engine.port_for(process, num, self.port)
35
33
  environment_variables = {'PORT' => port}.
36
34
  merge(engine.environment).
37
35
  merge(inline_variables(process.command))
@@ -51,7 +49,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
51
49
  private
52
50
  def create_directory(location)
53
51
  say "creating: #{location}"
54
- FileUtils.mkdir(location)
52
+ FileUtils.mkdir_p(location)
55
53
  end
56
54
 
57
55
  def inline_variables(command)
@@ -3,23 +3,21 @@ require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Upstart < Foreman::Export::Base
5
5
 
6
- def export(location, options={})
6
+ def export
7
7
  error("Must specify a location") unless location
8
8
 
9
9
  FileUtils.mkdir_p location
10
10
 
11
- app = options[:app] || File.basename(engine.directory)
12
- user = options[:user] || app
13
- log_root = options[:log] || "/var/log/#{app}"
14
- template_root = options[:template]
11
+ app = self.app || File.basename(engine.directory)
12
+ user = self.user || app
13
+ log_root = self.log || "/var/log/#{app}"
14
+ template_root = self.template
15
15
 
16
16
  Dir["#{location}/#{app}*.conf"].each do |file|
17
17
  say "cleaning up: #{file}"
18
18
  FileUtils.rm(file)
19
19
  end
20
20
 
21
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
22
-
23
21
  master_template = export_template("upstart", "master.conf.erb", template_root)
24
22
  master_config = ERB.new(master_template).result(binding)
25
23
  write_file "#{location}/#{app}.conf", master_config
@@ -27,13 +25,13 @@ class Foreman::Export::Upstart < Foreman::Export::Base
27
25
  process_template = export_template("upstart", "process.conf.erb", template_root)
28
26
 
29
27
  engine.procfile.entries.each do |process|
30
- next if (conc = concurrency[process.name]) < 1
28
+ next if (conc = self.concurrency[process.name]) < 1
31
29
  process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
32
30
  process_master_config = ERB.new(process_master_template).result(binding)
33
31
  write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
34
32
 
35
- 1.upto(concurrency[process.name]) do |num|
36
- port = engine.port_for(process, num, options[:port])
33
+ 1.upto(self.concurrency[process.name]) do |num|
34
+ port = engine.port_for(process, num, self.port)
37
35
  process_config = ERB.new(process_template).result(binding)
38
36
  write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
39
37
  end
@@ -0,0 +1,50 @@
1
+ module Foreman::Helpers
2
+ # Copied whole sale from, https://github.com/defunkt/resque/
3
+
4
+ # Given a word with dashes, returns a camel cased version of it.
5
+ #
6
+ # classify('job-name') # => 'JobName'
7
+ def classify(dashed_word)
8
+ dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
9
+ end
10
+
11
+ # Tries to find a constant with the name specified in the argument string:
12
+ #
13
+ # constantize("Module") # => Module
14
+ # constantize("Test::Unit") # => Test::Unit
15
+ #
16
+ # The name is assumed to be the one of a top-level constant, no matter
17
+ # whether it starts with "::" or not. No lexical context is taken into
18
+ # account:
19
+ #
20
+ # C = 'outside'
21
+ # module M
22
+ # C = 'inside'
23
+ # C # => 'inside'
24
+ # constantize("C") # => 'outside', same as ::C
25
+ # end
26
+ #
27
+ # NameError is raised when the constant is unknown.
28
+ def constantize(camel_cased_word)
29
+ camel_cased_word = camel_cased_word.to_s
30
+
31
+ if camel_cased_word.include?('-')
32
+ camel_cased_word = classify(camel_cased_word)
33
+ end
34
+
35
+ names = camel_cased_word.split('::')
36
+ names.shift if names.empty? || names.first.empty?
37
+
38
+ constant = Object
39
+ names.each do |name|
40
+ args = Module.method(:const_get).arity != 1 ? [false] : []
41
+
42
+ if constant.const_defined?(name, *args)
43
+ constant = constant.const_get(name)
44
+ else
45
+ constant = constant.const_missing(name)
46
+ end
47
+ end
48
+ constant
49
+ end
50
+ end
@@ -24,6 +24,24 @@ class Foreman::Process
24
24
  "%s.%s" % [ entry.name, num ]
25
25
  end
26
26
 
27
+ def kill(signal)
28
+ pid && Process.kill(signal, pid)
29
+ rescue Errno::ESRCH
30
+ false
31
+ end
32
+
33
+ def detach
34
+ pid && Process.detach(pid)
35
+ end
36
+
37
+ def alive?
38
+ kill(0)
39
+ end
40
+
41
+ def dead?
42
+ !alive?
43
+ end
44
+
27
45
  private
28
46
 
29
47
  def fork_with_io(command, basedir)
@@ -69,12 +87,10 @@ private
69
87
  end
70
88
 
71
89
  def with_environment(environment)
72
- old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
73
- environment.each { |k,v| ENV[k] = v }
74
- ret = yield
75
- ENV.clear
76
- old_env.each { |k,v| ENV[k] = v}
77
- ret
90
+ original = ENV.to_hash
91
+ ENV.update environment
92
+ yield
93
+ ensure
94
+ ENV.replace original
78
95
  end
79
-
80
96
  end
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.37.0.pre3"
3
+ VERSION = "0.37.0.pre4"
4
4
 
5
5
  end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
  require "foreman/cli"
3
3
 
4
- describe "Foreman::CLI" do
4
+ describe "Foreman::CLI", :fakefs do
5
5
  subject { Foreman::CLI.new }
6
6
 
7
7
  describe "start" do
@@ -30,7 +30,9 @@ describe "Foreman::CLI" do
30
30
  it "respects --env" do
31
31
  write_procfile
32
32
  write_env("envfile")
33
- mock.instance_of(Foreman::Export::Upstart).export("/upstart", { "env" => "envfile" })
33
+ mock_export = mock(Foreman::Export::Upstart)
34
+ mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
35
+ mock_export.export
34
36
  foreman %{ export upstart /upstart --env envfile }
35
37
  end
36
38
  end
@@ -49,7 +51,7 @@ describe "Foreman::CLI" do
49
51
 
50
52
  describe "with an invalid formatter" do
51
53
  it "prints an error" do
52
- mock_error(subject, "Unknown export format: invalidformatter.") do
54
+ mock_error(subject, "Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
53
55
  subject.export("invalidformatter")
54
56
  end
55
57
  end
@@ -60,7 +62,9 @@ describe "Foreman::CLI" do
60
62
 
61
63
  it "runs successfully" do
62
64
  dont_allow(subject).error
63
- mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
65
+ mock_export = mock(Foreman::Export::Upstart)
66
+ mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
67
+ mock_export.export
64
68
  subject.export("upstart", "/tmp/foo")
65
69
  end
66
70
  end
@@ -89,47 +93,47 @@ describe "Foreman::CLI" do
89
93
  end
90
94
  end
91
95
  end
92
-
96
+
93
97
  describe "run" do
94
98
  describe "with a valid Procfile" do
95
99
  before { write_procfile }
96
100
 
97
101
  describe "and a command" do
98
102
  let(:command) { ["ls", "-l"] }
99
-
103
+
100
104
  before(:each) do
101
105
  stub(subject).exec
102
106
  end
103
-
107
+
104
108
  it "should load the environment file" do
105
109
  write_env
106
110
  preserving_env do
107
111
  subject.run *command
108
112
  ENV["FOO"].should == "bar"
109
113
  end
110
-
114
+
111
115
  ENV["FOO"].should be_nil
112
116
  end
113
-
117
+
114
118
  it "should runute the command as a string" do
115
119
  mock(subject).exec(command.join(" "))
116
120
  subject.run *command
117
121
  end
118
122
  end
119
-
123
+
120
124
  describe "and a non-existent command" do
121
125
  let(:command) { "iuhtngrglhulhdfg" }
122
-
126
+
123
127
  it "should print an error" do
124
128
  mock_error(subject, "command not found: #{command}") do
125
129
  subject.run command
126
130
  end
127
131
  end
128
132
  end
129
-
133
+
130
134
  describe "and a non-executable command" do
131
135
  let(:command) { __FILE__ }
132
-
136
+
133
137
  it "should print an error" do
134
138
  mock_error(subject, "not executable: #{command}") do
135
139
  subject.run command
@@ -1,7 +1,7 @@
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
7
  describe "initialize" do
@@ -3,22 +3,27 @@ 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")
16
+ bluepill.export
16
17
  normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
17
18
  end
18
19
 
19
- it "exports to the filesystem with concurrency" do
20
- bluepill.export("/tmp/init", :concurrency => "alpha=2")
21
-
22
- normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
20
+ context "with concurrency" do
21
+ let(:options) { Hash[:concurrency => "alpha=2"] }
22
+
23
+ it "exports to the filesystem with concurrency" do
24
+ bluepill.export
25
+ normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
26
+ end
23
27
  end
24
- end
28
+
29
+ end
@@ -3,34 +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
15
  it "exports to the filesystem" do
16
16
  FileUtils.mkdir_p('/tmp/init')
17
- runit.export('/tmp/init', :concurrency => "alpha=2,bravo=1")
18
-
17
+
18
+ runit.export
19
+
19
20
  File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
20
- File.read("/tmp/init/app-alpha-1/log/run").should ==
21
+ File.read("/tmp/init/app-alpha-1/log/run").should ==
21
22
  example_export_file('runit/app-alpha-1-log-run')
22
23
  File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
23
24
  File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
24
-
25
+
25
26
  File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
26
- File.read("/tmp/init/app-alpha-2/log/run").should ==
27
+ File.read("/tmp/init/app-alpha-2/log/run").should ==
27
28
  example_export_file('runit/app-alpha-2-log-run')
28
29
  File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
29
30
  File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
30
-
31
+
31
32
  File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
32
- File.read("/tmp/init/app-bravo-1/log/run").should ==
33
+ File.read("/tmp/init/app-bravo-1/log/run").should ==
33
34
  example_export_file('runit/app-bravo-1-log-run')
34
35
  File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
35
36
  end
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
@@ -3,16 +3,17 @@ 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")
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")
@@ -21,18 +22,23 @@ describe Foreman::Export::Upstart do
21
22
  File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
22
23
  end
23
24
 
24
- it "exports to the filesystem with concurrency" do
25
- upstart.export("/tmp/init", :concurrency => "alpha=2")
25
+ context "with concurrency" do
26
+ let(:options) { Hash[:concurrency => "alpha=2"] }
26
27
 
27
- File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
28
- File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
29
- File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
30
- File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
31
- File.exists?("/tmp/init/app-bravo-1.conf").should == false
28
+ it "exports to the filesystem with concurrency" do
29
+ upstart.export
30
+
31
+ File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
32
+ File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
33
+ File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
34
+ File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
35
+ File.exists?("/tmp/init/app-bravo-1.conf").should == false
36
+ end
32
37
  end
33
38
 
34
39
  context "with alternate templates" do
35
40
  let(:template_root) { "/tmp/alternate" }
41
+ let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
36
42
 
37
43
  before do
38
44
  FileUtils.mkdir_p template_root
@@ -40,7 +46,7 @@ describe Foreman::Export::Upstart do
40
46
  end
41
47
 
42
48
  it "can export with alternate template files" do
43
- upstart.export("/tmp/init", :template => template_root)
49
+ upstart.export
44
50
 
45
51
  File.read("/tmp/init/app.conf").should == "alternate_template\n"
46
52
  end
@@ -61,7 +67,7 @@ describe Foreman::Export::Upstart do
61
67
  end
62
68
 
63
69
  it "can export with alternate template files" do
64
- upstart.export("/tmp/init")
70
+ upstart.export
65
71
 
66
72
  File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
67
73
  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
+ 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
data/spec/foreman_spec.rb CHANGED
@@ -8,13 +8,8 @@ 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
-
11
+ describe "::load_env!(env_file)", :fakefs do
16
12
  after do
17
- FakeFS.deactivate!
18
13
  ENV['FOO'] = nil
19
14
  end
20
15
 
@@ -22,7 +17,7 @@ describe Foreman do
22
17
  File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
23
18
  Foreman.load_env!("/tmp/env1")
24
19
  ENV['FOO'].should == 'bar'
25
- end
20
+ end
26
21
 
27
22
  it "should assume env_file in ./.env" do
28
23
  File.open("./.env", "w") { |f| f.puts("FOO=bar") }
data/spec/helper_spec.rb CHANGED
@@ -3,16 +3,16 @@ require "spec_helper"
3
3
  describe "spec helpers" do
4
4
  describe "#preserving_env" do
5
5
  after { ENV.delete "FOO" }
6
-
6
+
7
7
  it "should remove added environment vars" do
8
8
  preserving_env { ENV["FOO"] = "baz" }
9
9
  ENV["FOO"].should == nil
10
10
  end
11
-
11
+
12
12
  it "should reset modified environment vars" do
13
13
  ENV["FOO"] = "bar"
14
14
  preserving_env { ENV["FOO"] = "baz"}
15
15
  ENV["FOO"].should == "bar"
16
16
  end
17
17
  end
18
- end
18
+ end
data/spec/spec_helper.rb CHANGED
@@ -76,10 +76,11 @@ end
76
76
  def normalize_space(s)
77
77
  s.gsub(/\n[\n\s]*/, "\n")
78
78
  end
79
-
79
+
80
80
  RSpec.configure do |config|
81
+ config.treat_symbols_as_metadata_keys_with_true_values = true
81
82
  config.color_enabled = true
82
83
  config.order = 'rand'
83
- config.include FakeFS::SpecHelpers
84
+ config.include FakeFS::SpecHelpers, :fakefs
84
85
  config.mock_with :rr
85
86
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.37.0.pre3
4
+ version: 0.37.0.pre4
5
5
  prerelease: 7
6
6
  platform: mingw32
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-23 00:00:00.000000000 Z
12
+ date: 2012-01-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: term-ansicolor
16
- requirement: &70266822861340 !ruby/object:Gem::Requirement
16
+ requirement: &70246169986640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.7
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70266822861340
24
+ version_requirements: *70246169986640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: thor
27
- requirement: &70266822860460 !ruby/object:Gem::Requirement
27
+ requirement: &70246169985900 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.13.6
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70266822860460
35
+ version_requirements: *70246169985900
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: win32console
38
- requirement: &70266822859720 !ruby/object:Gem::Requirement
38
+ requirement: &70246169984620 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 1.3.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70266822859720
46
+ version_requirements: *70246169984620
47
47
  description: Process manager for applications with multiple components
48
48
  email: ddollar@gmail.com
49
49
  executables:
@@ -73,6 +73,7 @@ files:
73
73
  - lib/foreman/export/runit.rb
74
74
  - lib/foreman/export/upstart.rb
75
75
  - lib/foreman/export.rb
76
+ - lib/foreman/helpers.rb
76
77
  - lib/foreman/process.rb
77
78
  - lib/foreman/procfile.rb
78
79
  - lib/foreman/procfile_entry.rb
@@ -125,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
126
  version: 1.3.1
126
127
  requirements: []
127
128
  rubyforge_project:
128
- rubygems_version: 1.8.10
129
+ rubygems_version: 1.8.15
129
130
  signing_key:
130
131
  specification_version: 3
131
132
  summary: Process manager for applications with multiple components