foreman 0.37.0.pre5 → 0.37.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/foreman/cli.rb CHANGED
@@ -40,22 +40,11 @@ class Foreman::CLI < Thor
40
40
  method_option :port, :type => :numeric, :aliases => "-p"
41
41
  method_option :user, :type => :string, :aliases => "-u"
42
42
  method_option :template, :type => :string, :aliases => "-t"
43
- method_option :concurrency, :type => :string, :aliases => "-c",
44
- :banner => '"alpha=5,bar=3"'
43
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
45
44
 
46
45
  def export(format, location=nil)
47
46
  check_procfile!
48
-
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('-', '_') }')."
57
- end
58
-
47
+ formatter = Foreman::Export.formatter(format)
59
48
  formatter.new(location, engine, options).export
60
49
  rescue Foreman::Export::Exception => ex
61
50
  error ex.message
@@ -65,7 +54,7 @@ class Foreman::CLI < Thor
65
54
 
66
55
  def check
67
56
  error "no processes defined" unless engine.procfile.entries.length > 0
68
- display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
57
+ puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
69
58
  end
70
59
 
71
60
  desc "run COMMAND", "Run a command using your application's environment"
@@ -95,19 +84,11 @@ private ######################################################################
95
84
  options[:procfile] || "Procfile"
96
85
  end
97
86
 
98
- def display(message)
99
- puts message
100
- end
101
-
102
87
  def error(message)
103
88
  puts "ERROR: #{message}"
104
89
  exit 1
105
90
  end
106
91
 
107
- def procfile_exists?(procfile)
108
- File.exist?(procfile)
109
- end
110
-
111
92
  def options
112
93
  original_options = super
113
94
  return original_options unless File.exists?(".foreman")
@@ -78,9 +78,17 @@ private ######################################################################
78
78
  end
79
79
 
80
80
  def terminate_gracefully
81
+ return if @terminating
82
+ @terminating = true
81
83
  info "sending SIGTERM to all processes"
82
84
  kill_all "SIGTERM"
83
- Timeout.timeout(5) { Process.waitall }
85
+ Timeout.timeout(5) do
86
+ while running_processes.length > 0
87
+ pid, status = Process.wait2
88
+ process = running_processes.delete(pid)
89
+ info "process terminated", process.name
90
+ end
91
+ end
84
92
  rescue Timeout::Error
85
93
  info "sending SIGKILL to all processes"
86
94
  kill_all "SIGKILL"
@@ -112,16 +120,16 @@ private ######################################################################
112
120
  process = running_processes.delete(pid)
113
121
  info "process terminated", process.name
114
122
  terminate_gracefully
115
- kill_all
116
123
  rescue Errno::ECHILD
117
124
  end
118
125
 
119
126
  def info(message, name="system", color=Term::ANSIColor.white)
120
- print color
121
- print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
122
- print Term::ANSIColor.reset
123
- print message.chomp
124
- puts ""
127
+ output = ""
128
+ output += color
129
+ output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
130
+ output += Term::ANSIColor.reset
131
+ output += message.chomp
132
+ puts output
125
133
  end
126
134
 
127
135
  def print(message=nil)
@@ -1,9 +1,30 @@
1
1
  require "foreman"
2
+ require "foreman/helpers"
2
3
 
3
4
  module Foreman::Export
5
+ extend Foreman::Helpers
6
+
4
7
  class Exception < ::Exception; end
8
+
9
+ def self.formatter(format)
10
+ begin
11
+ require "foreman/export/#{ format.tr('-', '_') }"
12
+ classy_format = classify(format)
13
+ formatter = constantize("Foreman::Export::#{ classy_format }")
14
+ rescue NameError => ex
15
+ error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
16
+ rescue LoadError => ex
17
+ error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
18
+ end
19
+ end
20
+
21
+ def self.error(message)
22
+ raise Foreman::Export::Exception.new(message)
23
+ end
24
+
5
25
  end
6
26
 
27
+
7
28
  require "foreman/export/base"
8
29
  require "foreman/export/inittab"
9
30
  require "foreman/export/upstart"
@@ -24,12 +24,12 @@ class Foreman::Export::Inittab < Foreman::Export::Base
24
24
 
25
25
  inittab = inittab.join("\n") + "\n"
26
26
 
27
- if fname
27
+ if location == "-"
28
+ puts inittab
29
+ else
28
30
  FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
29
31
  FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
30
- write_file(fname, inittab)
31
- else
32
- puts inittab
32
+ write_file(location, inittab)
33
33
  end
34
34
  end
35
35
 
@@ -6,9 +6,8 @@ module Foreman::Helpers
6
6
  # classify('job-name') # => 'JobName'
7
7
  def classify(dashed_word)
8
8
  dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
9
- end
9
+ end # Tries to find a constant with the name specified in the argument string:
10
10
 
11
- # Tries to find a constant with the name specified in the argument string:
12
11
  #
13
12
  # constantize("Module") # => Module
14
13
  # constantize("Test::Unit") # => Test::Unit
@@ -28,10 +27,6 @@ module Foreman::Helpers
28
27
  def constantize(camel_cased_word)
29
28
  camel_cased_word = camel_cased_word.to_s
30
29
 
31
- if camel_cased_word.include?('-')
32
- camel_cased_word = classify(camel_cased_word)
33
- end
34
-
35
30
  names = camel_cased_word.split('::')
36
31
  names.shift if names.empty? || names.first.empty?
37
32
 
@@ -47,4 +42,4 @@ module Foreman::Helpers
47
42
  end
48
43
  constant
49
44
  end
50
- end
45
+ end
@@ -63,7 +63,7 @@ private
63
63
  $stderr.reopen writer
64
64
  reader.close
65
65
  exec Foreman.runner, "-d", basedir, command
66
- end
66
+ end
67
67
  end
68
68
  [ reader, pid ]
69
69
  end
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.37.0.pre5"
3
+ VERSION = "0.37.0"
4
4
 
5
5
  end
@@ -27,6 +27,15 @@ describe "Foreman::CLI", :fakefs do
27
27
 
28
28
  describe "export" do
29
29
  describe "options" do
30
+ it "uses .foreman" do
31
+ write_procfile
32
+ File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
33
+ mock_export = mock(Foreman::Export::Upstart)
34
+ mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
35
+ mock_export.export
36
+ foreman %{ export upstart /upstart }
37
+ end
38
+
30
39
  it "respects --env" do
31
40
  write_procfile
32
41
  write_env("envfile")
@@ -49,10 +58,18 @@ describe "Foreman::CLI", :fakefs do
49
58
  describe "with a Procfile" do
50
59
  before(:each) { write_procfile }
51
60
 
52
- describe "with an invalid formatter" do
61
+ describe "with a formatter with a generic error" do
62
+ before do
63
+ mock(Foreman::Export).formatter("errorful") { Class.new(Foreman::Export::Base) do
64
+ def export
65
+ raise Foreman::Export::Exception.new("foo")
66
+ end
67
+ end }
68
+ end
69
+
53
70
  it "prints an error" do
54
- mock_error(subject, "Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
55
- subject.export("invalidformatter")
71
+ mock_error(subject, "foo") do
72
+ subject.export("errorful")
56
73
  end
57
74
  end
58
75
  end
@@ -76,7 +93,7 @@ describe "Foreman::CLI", :fakefs do
76
93
  before { write_procfile }
77
94
 
78
95
  it "displays the jobs" do
79
- mock(subject).display("valid procfile detected (alpha, bravo)")
96
+ mock(subject).puts("valid procfile detected (alpha, bravo)")
80
97
  subject.check
81
98
  end
82
99
  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
@@ -17,7 +17,14 @@ describe Foreman::Export::Bluepill, :fakefs do
17
17
  normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
18
18
  end
19
19
 
20
- context "with concurrency" do
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
21
28
  let(:options) { Hash[:concurrency => "alpha=2"] }
22
29
 
23
30
  it "exports to the filesystem with concurrency" do
@@ -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
@@ -22,6 +22,17 @@ describe Foreman::Export::Upstart, :fakefs do
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
+
25
36
  context "with concurrency" do
26
37
  let(:options) { Hash[:concurrency => "alpha=2"] }
27
38
 
@@ -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
data/spec/foreman_spec.rb CHANGED
@@ -25,4 +25,10 @@ describe Foreman do
25
25
  ENV['FOO'].should == 'bar'
26
26
  end
27
27
  end
28
+
29
+ describe "runner" do
30
+ it "should exist" do
31
+ File.exists?(Foreman.runner).should == true
32
+ end
33
+ end
28
34
  end
@@ -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 -----
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,20 @@
1
1
  require "rubygems"
2
+
3
+ require "simplecov"
4
+ SimpleCov.start do
5
+ add_filter "/spec/"
6
+ end
7
+
2
8
  require "rspec"
3
9
  require "fakefs/safe"
4
10
  require "fakefs/spec_helpers"
5
11
 
6
12
  $:.unshift File.expand_path("../../lib", __FILE__)
7
13
 
14
+ def mock_export_error(message)
15
+ lambda { yield }.should raise_error(Foreman::Export::Exception, message)
16
+ end
17
+
8
18
  def mock_error(subject, message)
9
19
  mock_exit do
10
20
  mock(subject).puts("ERROR: #{message}")
@@ -37,9 +47,11 @@ def write_procfile(procfile="Procfile", alpha_env="")
37
47
  File.expand_path(procfile)
38
48
  end
39
49
 
40
- def write_env(env=".env")
50
+ def write_env(env=".env", options={"FOO"=>"bar"})
41
51
  File.open(env, "w") do |file|
42
- file.puts "FOO=bar"
52
+ options.each do |key, val|
53
+ file.puts "#{key}=#{val}"
54
+ end
43
55
  end
44
56
  end
45
57
 
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.37.0.pre5
5
- prerelease: 7
4
+ version: 0.37.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - David Dollar
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-24 00:00:00.000000000 Z
12
+ date: 2012-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: term-ansicolor
16
- requirement: &70240847722740 !ruby/object:Gem::Requirement
16
+ requirement: &70149919501700 !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: *70240847722740
24
+ version_requirements: *70149919501700
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: thor
27
- requirement: &70240847721920 !ruby/object:Gem::Requirement
27
+ requirement: &70149919500700 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 0.13.6
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70240847721920
35
+ version_requirements: *70149919500700
36
36
  description: Process manager for applications with multiple components
37
37
  email: ddollar@gmail.com
38
38
  executables:
@@ -72,15 +72,20 @@ files:
72
72
  - README.md
73
73
  - spec/foreman/cli_spec.rb
74
74
  - spec/foreman/engine_spec.rb
75
+ - spec/foreman/export/base_spec.rb
75
76
  - spec/foreman/export/bluepill_spec.rb
77
+ - spec/foreman/export/inittab_spec.rb
76
78
  - spec/foreman/export/runit_spec.rb
77
79
  - spec/foreman/export/upstart_spec.rb
78
80
  - spec/foreman/export_spec.rb
81
+ - spec/foreman/helpers_spec.rb
79
82
  - spec/foreman/process_spec.rb
80
83
  - spec/foreman_spec.rb
81
84
  - spec/helper_spec.rb
82
85
  - spec/resources/export/bluepill/app-concurrency.pill
83
86
  - spec/resources/export/bluepill/app.pill
87
+ - spec/resources/export/inittab/inittab.concurrency
88
+ - spec/resources/export/inittab/inittab.default
84
89
  - spec/resources/export/runit/app-alpha-1-log-run
85
90
  - spec/resources/export/runit/app-alpha-1-run
86
91
  - spec/resources/export/runit/app-alpha-2-log-run
@@ -110,9 +115,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
115
  required_rubygems_version: !ruby/object:Gem::Requirement
111
116
  none: false
112
117
  requirements:
113
- - - ! '>'
118
+ - - ! '>='
114
119
  - !ruby/object:Gem::Version
115
- version: 1.3.1
120
+ version: '0'
116
121
  requirements: []
117
122
  rubyforge_project:
118
123
  rubygems_version: 1.8.15