diff --git a/.gitignore b/.gitignore index 66c3543..93e71e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +coverage example/log/* + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..f848eb3 --- /dev/null +++ b/Rakefile @@ -0,0 +1,60 @@ +require "rubygems" +require "rake" +require "rspec/core/rake_task" + +$:.unshift File.expand_path("../lib", __FILE__) +require "foreman" + +task :default => :spec + +desc "Run all specs" +Rspec::Core::RakeTask.new(:spec) do |t| + t.pattern = 'spec/**/*_spec.rb' +end + +desc "Generate RCov code coverage report" +task :rcov => "rcov:build" do + %x{ open coverage/index.html } +end + +Rspec::Core::RakeTask.new("rcov:build") do |t| + t.pattern = 'spec/**/*_spec.rb' + t.rcov = true + t.rcov_opts = [ "--exclude", Gem.default_dir , "--exclude", "spec" ] +end + +###################################################### + +begin + require 'jeweler' + Jeweler::Tasks.new do |s| + s.name = "foreman" + s.version = Foreman::VERSION + + s.summary = "Process manager for applications with multiple components" + s.description = s.summary + s.author = "David Dollar" + s.email = "ddollar@gmail.com" + s.homepage = "http://github.com/ddollar/foreman" + + s.platform = Gem::Platform::RUBY + s.has_rdoc = false + + s.files = %w(Rakefile README.md) + Dir["{bin,lib,spec}/**/*"] + s.require_path = "lib" + + s.bindir = "bin" + s.executables = Dir["bin/*"] + s.default_executable = "foreman" + + s.add_development_dependency 'fakefs', '~> 0.2.1' + s.add_development_dependency 'rake', '~> 0.8.7' + s.add_development_dependency 'rcov', '~> 0.9.8' + s.add_development_dependency 'rr', '~> 0.10.11' + s.add_development_dependency 'rspec', '~> 2.0.0' + + s.add_dependency 'thor', '~> 0.13.6' + end +rescue LoadError + puts "Jeweler not available. Install it with: sudo gem install jeweler" +end diff --git a/autotest/discover.rb b/autotest/discover.rb new file mode 100644 index 0000000..cd6892c --- /dev/null +++ b/autotest/discover.rb @@ -0,0 +1 @@ +Autotest.add_discovery { "rspec2" } diff --git a/lib/foreman/cli.rb b/lib/foreman/cli.rb index 5ee9dd9..4519209 100644 --- a/lib/foreman/cli.rb +++ b/lib/foreman/cli.rb @@ -9,18 +9,18 @@ class Foreman::CLI < Thor desc "start [PROCFILE]", "Run the app described in PROCFILE" def start(procfile="Procfile") - error "#{procfile} does not exist." unless File.exist?(procfile) + error "#{procfile} does not exist." unless procfile_exists?(procfile) Foreman::Engine.new(procfile).start end desc "export APP [PROCFILE] [FORMAT]", "Export the app described in PROCFILE as APP to another FORMAT" def export(app, procfile="Procfile", format="upstart") - error "#{procfile} does not exist." unless File.exist?(procfile) + error "#{procfile} does not exist." unless procfile_exists?(procfile) formatter = case format when "upstart" then Foreman::Export::Upstart - else error "Unknown export format: #{format}" + else error "Unknown export format: #{format}." end formatter.new(Foreman::Engine.new(procfile)).export(app) @@ -29,20 +29,7 @@ class Foreman::CLI < Thor desc "scale APP PROCESS AMOUNT", "Change the concurrency of a given process type" def scale(app, process, amount) - config = Foreman::Configuration.new(app) - - amount = amount.to_i - old_amount = config.processes[process] - - config.scale(process, amount) - - if (old_amount < amount) - ((old_amount+1)..amount).each { |num| system "start #{app}-#{process} NUM=#{num}" } - elsif (amount < old_amount) - ((amount+1)..old_amount).each { |num| system "stop #{app}-#{process} NUM=#{num}" } - end - - config.write + Foreman::Configuration.new(app).scale(process, amount) end private ###################################################################### @@ -52,4 +39,8 @@ private ###################################################################### exit 1 end + def procfile_exists?(procfile) + File.exist?(procfile) + end + end diff --git a/lib/foreman/configuration.rb b/lib/foreman/configuration.rb index 7c4f39a..949c2f5 100644 --- a/lib/foreman/configuration.rb +++ b/lib/foreman/configuration.rb @@ -12,7 +12,17 @@ class Foreman::Configuration end def scale(process, amount) + old_amount = processes[process] processes[process] = amount.to_i + amount = amount.to_i + + if (old_amount < amount) + ((old_amount + 1) .. amount).each { |num| run "start #{app}-#{process} NUM=#{num}" } + elsif (amount < old_amount) + ((amount + 1) .. old_amount).each { |num| run "stop #{app}-#{process} NUM=#{num}" } + end + + write end def write @@ -31,10 +41,14 @@ private ###################################################################### accum.update(key => value) end config["#{app}_processes"].split(" ").each do |process| - scale(process, config["#{app}_#{process}"]) + processes[process] = config["#{app}_#{process}"].to_i end end + def run(command) + system command + end + def write_file(filename, contents) File.open(filename, "w") do |file| file.puts contents diff --git a/lib/foreman/engine.rb b/lib/foreman/engine.rb index d66991e..19788e5 100644 --- a/lib/foreman/engine.rb +++ b/lib/foreman/engine.rb @@ -7,7 +7,7 @@ class Foreman::Engine attr_reader :directory def initialize(procfile) - @procfile = File.read(procfile) + @procfile = read_procfile(procfile) @directory = File.expand_path(File.dirname(procfile)) end @@ -76,6 +76,10 @@ private ###################################################################### end end + def read_procfile(procfile) + File.read(procfile) + end + def running_processes @running_processes ||= {} end diff --git a/spec/foreman/cli_spec.rb b/spec/foreman/cli_spec.rb new file mode 100644 index 0000000..0641dc0 --- /dev/null +++ b/spec/foreman/cli_spec.rb @@ -0,0 +1,77 @@ +require "spec_helper" +require "foreman/cli" + +describe "Foreman::CLI" do + subject { Foreman::CLI.new } + + describe "start" do + #let(:engine) { stub_engine } + + describe "with a non-existent Procifile" do + it "prints an error" do + mock_error(subject, "Procfile does not exist.") do + dont_allow.instance_of(Foreman::Engine).start + subject.start + end + end + end + + describe "with a Procfile" do + before(:each) { write_procfile } + + it "runs successfully" do + dont_allow(subject).error + mock.instance_of(Foreman::Engine).start + subject.start + end + end + end + + describe "export" do + describe "with a non-existent Procifile" do + it "prints an error" do + mock_error(subject, "Procfile does not exist.") do + dont_allow.instance_of(Foreman::Engine).export + subject.export("testapp") + end + end + end + + describe "with a Procfile" do + before(:each) { write_procfile } + + describe "with an invalid formatter" do + it "prints an error" do + mock_error(subject, "Unknown export format: invalidformatter.") do + subject.export("testapp", "Procfile", "invalidformatter") + end + end + end + + describe "with a valid config" do + before(:each) { write_foreman_config("testapp") } + + it "runs successfully" do + dont_allow(subject).error + subject.export("testapp") + end + end + end + end + + describe "scale" do + describe "without an existing configuration" do + # TODO + end + + describe "with an existing configuration" do + before(:each) { write_foreman_config("testapp") } + + it "scales the specified process" do + mock.instance_of(Foreman::Configuration).scale("testprocess", "2") + subject.scale("testapp", "testprocess", "2") + end + end + end + +end diff --git a/spec/foreman/configuration_spec.rb b/spec/foreman/configuration_spec.rb new file mode 100644 index 0000000..7120dc3 --- /dev/null +++ b/spec/foreman/configuration_spec.rb @@ -0,0 +1,2 @@ +require "spec_helper" +require "foreman/configuration" diff --git a/spec/foreman/engine_spec.rb b/spec/foreman/engine_spec.rb new file mode 100644 index 0000000..b778680 --- /dev/null +++ b/spec/foreman/engine_spec.rb @@ -0,0 +1,2 @@ +require "spec_helper" +require "foreman/engine" diff --git a/spec/foreman/export/upstart_spec.rb b/spec/foreman/export/upstart_spec.rb new file mode 100644 index 0000000..16a3323 --- /dev/null +++ b/spec/foreman/export/upstart_spec.rb @@ -0,0 +1,2 @@ +require "spec_helper" +require "foreman/export/upstart" diff --git a/spec/foreman/export_spec.rb b/spec/foreman/export_spec.rb new file mode 100644 index 0000000..bcfd80a --- /dev/null +++ b/spec/foreman/export_spec.rb @@ -0,0 +1,2 @@ +require "spec_helper" +require "foreman/export" diff --git a/spec/foreman/process_spec.rb b/spec/foreman/process_spec.rb new file mode 100644 index 0000000..2fb5841 --- /dev/null +++ b/spec/foreman/process_spec.rb @@ -0,0 +1,2 @@ +require "spec_helper" +require "foreman/process" diff --git a/spec/foreman_spec.rb b/spec/foreman_spec.rb new file mode 100644 index 0000000..b0a0286 --- /dev/null +++ b/spec/foreman_spec.rb @@ -0,0 +1,11 @@ +require "spec_helper" +require "foreman" + +describe Foreman do + + describe "VERSION" do + subject { Foreman::VERSION } + it { should be_a String } + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..afa5f75 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,55 @@ +require "fakefs/spec_helpers" +require "rspec" + +$:.unshift "lib" + +# def stub_configuration +# config = mock(Foreman::Configuration) +# Foreman::Configuration.stub(:new).and_return(config) +# config +# end +# +# +# def stub_export_upstart +# upstart = mock(Foreman::Export::Upstart) +# Foreman::Export::Upstart.stub(:new).and_return(upstart) +# upstart +# end + +def mock_error(subject, message) + mock_exit do + mock(subject).puts("ERROR: #{message}") + yield + end +end + +def mock_exit(&block) + block.should raise_error(SystemExit) +end + +# def stub_engine +# engine = mock(Foreman::Engine) +# stub(Foreman::Engine).new { engine } +# engine +# end +# +def write_foreman_config(app) + File.open("/etc/foreman/#{app}.conf", "w") do |file| + file.puts %{#{app}_processes="alpha bravo"} + file.puts %{#{app}_alpha="1"} + file.puts %{#{app}_bravo="2"} + end +end + +def write_procfile(procfile="Procfile") + File.open(procfile, "w") do |file| + file.puts "alpha ./alpha" + file.puts "bravo ./bravo" + end +end + +Rspec.configure do |config| + config.color_enabled = true + config.include FakeFS::SpecHelpers + config.mock_with :rr +end