foreman 0.46.0 → 0.47.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -27,6 +27,12 @@ Manage Procfile-based applications
27
27
  * [wiki](http://github.com/ddollar/foreman/wiki)
28
28
  * [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
29
29
 
30
+ ## Ports
31
+
32
+ * [shoreman](https://github.com/hecticjeff/shoreman) - shell
33
+ * [honcho](https://github.com/nickstenning/honcho) - python
34
+ * [norman](https://github.com/josh/norman) - node.js
35
+
30
36
  ## Authors
31
37
 
32
38
  #### Created and maintained by
data/bin/foreman-runner CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/bin/sh
2
2
  #
3
- #/ Usage: foreman-runner [-d <dir>] <command>
3
+ #/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
4
4
  #/
5
5
  #/ Run a command with exec, optionally changing directory first
6
6
 
@@ -27,10 +27,6 @@ done
27
27
 
28
28
  shift $((OPTIND-1))
29
29
 
30
- command=$1
30
+ [ -z "$1" ] && usage
31
31
 
32
- if [ -z "$1" ]; then
33
- usage
34
- fi
35
-
36
- exec $1
32
+ exec "$@"
@@ -11,8 +11,9 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
11
11
 
12
12
  process.working_dir = "<%= engine.directory %>"
13
13
  process.daemonize = true
14
- process.environment = {"PORT" => "<%= port %>"}
14
+ process.environment = {"PORT" => "<%= port %>"<% engine.environment.each_pair do |var,env| %> , "<%= var.upcase %>" => "<%= env %>" <% end %>}
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 = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
18
19
 
@@ -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}-#{process.name}-#{num}" %></string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string><%= process.command %></string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string><%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log</string>
17
+ <key>UserName</key>
18
+ <string><%= user %></string>
19
+ <key>WorkingDirectory</key>
20
+ <string><%= engine.directory %></string>
21
+ </dict>
22
+ </plist>
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
2
2
  stop on stopping <%= app %>-<%= process.name %>
3
3
  respawn
4
4
 
5
- exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
5
+ exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
data/lib/foreman/cli.rb CHANGED
@@ -2,6 +2,7 @@ require "foreman"
2
2
  require "foreman/helpers"
3
3
  require "foreman/engine"
4
4
  require "foreman/export"
5
+ require "shellwords"
5
6
  require "thor"
6
7
  require "yaml"
7
8
 
@@ -59,12 +60,12 @@ class Foreman::CLI < Thor
59
60
  puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
60
61
  end
61
62
 
62
- desc "run COMMAND", "Run a command using your application's environment"
63
+ desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
63
64
 
64
65
  def run(*args)
65
66
  engine.apply_environment!
66
67
  begin
67
- exec args.join(" ")
68
+ exec args.shelljoin
68
69
  rescue Errno::EACCES
69
70
  error "not executable: #{args.first}"
70
71
  rescue Errno::ENOENT
@@ -36,6 +36,7 @@ class Foreman::Engine
36
36
 
37
37
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
38
38
  trap("INT") { puts "SIGINT received"; terminate_gracefully }
39
+ trap("HUP") { puts "SIGHUP received"; terminate_gracefully }
39
40
 
40
41
  assign_colors
41
42
  spawn_processes
@@ -84,7 +85,7 @@ private ######################################################################
84
85
  end
85
86
 
86
87
  def base_port
87
- options[:port] || 5000
88
+ options[:port] || environment["PORT"] || ENV["PORT"] || 5000
88
89
  end
89
90
 
90
91
  def kill_all(signal="SIGTERM")
@@ -31,4 +31,5 @@ require "foreman/export/upstart"
31
31
  require "foreman/export/bluepill"
32
32
  require "foreman/export/runit"
33
33
  require "foreman/export/supervisord"
34
+ require "foreman/export/launchd"
34
35
 
@@ -48,4 +48,19 @@ private ######################################################################
48
48
  end
49
49
  end
50
50
 
51
+ # Quote a string to be used on the command line. Backslashes are escapde to \\ and quotes
52
+ # escaped to \"
53
+ #
54
+ # str - string to be quoted
55
+ #
56
+ # Examples
57
+ #
58
+ # shell_quote("FB|123\"\\1")
59
+ # # => "\"FB|123\"\\"\\\\1\""
60
+ #
61
+ # Returns the the escaped string surrounded by quotes
62
+ def shell_quote(str)
63
+ "\"#{str.gsub(/\\/){ '\\\\' }.gsub(/["]/){ "\\\"" }}\""
64
+ end
65
+
51
66
  end
@@ -0,0 +1,27 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Launchd < Foreman::Export::Base
5
+
6
+ def export
7
+ error("Must specify a location") unless location
8
+
9
+ app = self.app || File.basename(engine.directory)
10
+ user = self.user || app
11
+ log_root = self.log || "/var/log/#{app}"
12
+ template_root = self.template
13
+
14
+ FileUtils.mkdir_p(location)
15
+
16
+ engine.procfile.entries.each do |process|
17
+ 1.upto(self.concurrency[process.name]) do |num|
18
+
19
+ master_template = export_template("launchd", "launchd.plist.erb", template_root)
20
+ master_config = ERB.new(master_template).result(binding)
21
+ write_file "#{location}/#{app}-#{process.name}-#{num}.plist", master_config
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -62,7 +62,7 @@ private
62
62
  $stdout.reopen writer
63
63
  $stderr.reopen writer
64
64
  reader.close
65
- exec Foreman.runner, "-d", basedir, command
65
+ exec Foreman.runner, "-d", basedir, *command.shellsplit
66
66
  end
67
67
  end
68
68
  [ reader, pid ]
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.46.0"
3
+ VERSION = "0.47.0"
4
4
 
5
5
  end
data/man/foreman.1 CHANGED
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "FOREMAN" "1" "April 2012" "Foreman 0.45.0" "Foreman Manual"
4
+ .TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBforeman\fR \- manage Procfile\-based applications
@@ -144,7 +144,7 @@ describe "Foreman::CLI", :fakefs do
144
144
  before { write_procfile }
145
145
 
146
146
  describe "and a command" do
147
- let(:command) { ["ls", "-l"] }
147
+ let(:command) { ["ls", "-l", "foo bar"] }
148
148
 
149
149
  before(:each) do
150
150
  stub(subject).exec
@@ -160,8 +160,8 @@ describe "Foreman::CLI", :fakefs do
160
160
  ENV["FOO"].should be_nil
161
161
  end
162
162
 
163
- it "should runute the command as a string" do
164
- mock(subject).exec(command.join(" "))
163
+ it "should exec the argument list as a shell command" do
164
+ mock(subject).exec(command.shelljoin)
165
165
  subject.run *command
166
166
  end
167
167
  end
@@ -100,6 +100,13 @@ describe "Foreman::Engine", :fakefs do
100
100
  engine.start
101
101
  end
102
102
 
103
+ it "should set port from .env if specified" do
104
+ File.open(".env", "w") { |f| f.puts("PORT=8017") }
105
+ engine = Foreman::Engine.new("Procfile")
106
+ engine.send(:base_port).should == "8017"
107
+ engine.start
108
+ end
109
+
103
110
  it "should be loaded relative to the Procfile" do
104
111
  FileUtils.mkdir_p "/some/app"
105
112
  File.open("/some/app/.env", "w") { |f| f.puts("FOO=qoo") }
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/launchd"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Launchd, :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(:launchd) { Foreman::Export::Launchd.new("/tmp/init", engine, options) }
11
+
12
+ before(:each) { load_export_templates_into_fakefs("launchd") }
13
+ before(:each) { stub(launchd).say }
14
+
15
+ it "exports to the filesystem" do
16
+ launchd.export
17
+
18
+ normalize_space(File.read("/tmp/init/app-alpha-1.plist")).should == normalize_space(example_export_file("launchd/launchd-a.default"))
19
+
20
+ normalize_space(File.read("/tmp/init/app-bravo-1.plist")).should == normalize_space(example_export_file("launchd/launchd-b.default"))
21
+
22
+ end
23
+
24
+ end
@@ -33,6 +33,12 @@ describe Foreman::Export::Upstart, :fakefs do
33
33
  upstart.export
34
34
  end
35
35
 
36
+ it "quotes and escapes environment variables" do
37
+ engine.environment['KEY'] = 'd"\|d'
38
+ upstart.export
39
+ File.read("/tmp/init/app-alpha-1.conf").should include('KEY="d\"\\\\|d"')
40
+ end
41
+
36
42
  context "with concurrency" do
37
43
  let(:options) { Hash[:concurrency => "alpha=2"] }
38
44
 
@@ -121,11 +121,25 @@ describe Foreman::Process do
121
121
  output.should include('777')
122
122
  end
123
123
 
124
- it 'should handle arguments' do
125
- pending
124
+ it 'should handle multi-word arguments (old test)' do
125
+ # TODO: This test used to be marked pending; it now passes,
126
+ # but is very slow. The next test is a fast replacement.
126
127
  run %{ sh -c "trap '' TERM; sleep 10" }
127
128
  subject.should be_alive
128
129
  end
130
+
131
+ it 'should handle multi-word arguments' do
132
+ # We have to be a little clever here since Foreman will always
133
+ # print a status message that includes the command.
134
+ run %{ sh -c 'echo abcdef | tr a-c x | tr d-f y' }
135
+ output.should include('xxxyyy')
136
+ end
137
+
138
+ it 'should not clobber "$x"-subexpressions' do
139
+ pending 'this conflicts with the variable interpolation hack'
140
+ run %{ sh -c 'echo \$abcdef | tr \$ %' }
141
+ output.should include('%abcdef')
142
+ end
129
143
  end
130
144
  end
131
145
  end
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
13
13
  process.daemonize = true
14
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
 
@@ -31,6 +32,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
31
32
  process.daemonize = true
32
33
  process.environment = {"PORT" => "5001"}
33
34
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
35
+ process.stop_grace_time = 45.seconds
34
36
 
35
37
  process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
36
38
 
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
13
13
  process.daemonize = true
14
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
 
@@ -30,6 +31,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
30
31
  process.daemonize = true
31
32
  process.environment = {"PORT" => "5100"}
32
33
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
34
+ process.stop_grace_time = 45.seconds
33
35
 
34
36
  process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
35
37
 
@@ -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>
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.46.0
4
+ version: 0.47.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-02 00:00:00.000000000 Z
12
+ date: 2012-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &70104163807140 !ruby/object:Gem::Requirement
16
+ requirement: &70170917701380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 0.13.6
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70104163807140
24
+ version_requirements: *70170917701380
25
25
  description: Process manager for applications with multiple components
26
26
  email: ddollar@gmail.com
27
27
  executables:
@@ -38,6 +38,7 @@ files:
38
38
  - data/example/ticker
39
39
  - data/example/utf8
40
40
  - data/export/bluepill/master.pill.erb
41
+ - data/export/launchd/launchd.plist.erb
41
42
  - data/export/runit/log_run.erb
42
43
  - data/export/runit/run.erb
43
44
  - data/export/supervisord/app.conf.erb
@@ -51,6 +52,7 @@ files:
51
52
  - lib/foreman/export/base.rb
52
53
  - lib/foreman/export/bluepill.rb
53
54
  - lib/foreman/export/inittab.rb
55
+ - lib/foreman/export/launchd.rb
54
56
  - lib/foreman/export/runit.rb
55
57
  - lib/foreman/export/supervisord.rb
56
58
  - lib/foreman/export/upstart.rb
@@ -69,6 +71,7 @@ files:
69
71
  - spec/foreman/export/base_spec.rb
70
72
  - spec/foreman/export/bluepill_spec.rb
71
73
  - spec/foreman/export/inittab_spec.rb
74
+ - spec/foreman/export/launchd_spec.rb
72
75
  - spec/foreman/export/runit_spec.rb
73
76
  - spec/foreman/export/supervisord_spec.rb
74
77
  - spec/foreman/export/upstart_spec.rb
@@ -84,6 +87,8 @@ files:
84
87
  - spec/resources/export/bluepill/app.pill
85
88
  - spec/resources/export/inittab/inittab.concurrency
86
89
  - spec/resources/export/inittab/inittab.default
90
+ - spec/resources/export/launchd/launchd-a.default
91
+ - spec/resources/export/launchd/launchd-b.default
87
92
  - spec/resources/export/runit/app-alpha-1-log-run
88
93
  - spec/resources/export/runit/app-alpha-1-run
89
94
  - spec/resources/export/runit/app-alpha-2-log-run
@@ -114,18 +119,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
119
  - - ! '>='
115
120
  - !ruby/object:Gem::Version
116
121
  version: '0'
117
- segments:
118
- - 0
119
- hash: -4034722301368458418
120
122
  required_rubygems_version: !ruby/object:Gem::Requirement
121
123
  none: false
122
124
  requirements:
123
125
  - - ! '>='
124
126
  - !ruby/object:Gem::Version
125
127
  version: '0'
126
- segments:
127
- - 0
128
- hash: -4034722301368458418
129
128
  requirements: []
130
129
  rubyforge_project:
131
130
  rubygems_version: 1.8.11