proctor 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/Proctorfile +4 -0
  2. data/README.md +97 -2
  3. data/Rakefile +15 -1
  4. data/bin/proctor +92 -88
  5. data/lib/proctor/app_config.rb +23 -0
  6. data/lib/proctor/app_file.rb +40 -1
  7. data/lib/proctor/cmd_state.rb +39 -0
  8. data/lib/proctor/manager.rb +8 -2
  9. data/lib/proctor/presenter/list.rb +67 -0
  10. data/lib/proctor/presenter/render.rb +39 -0
  11. data/lib/proctor/presenter/show.rb +71 -0
  12. data/lib/proctor/service.rb +8 -0
  13. data/lib/proctor/template_config.rb +22 -0
  14. data/lib/proctor/template_file.rb +44 -0
  15. data/lib/proctor/version.rb +1 -1
  16. data/lib/proctor.rb +1 -1
  17. data/proctor/Proctorfile +44 -20
  18. data/proctor/templates/foreman_default_n.erb +9 -0
  19. data/proctor/templates/monit_default_s.erb +6 -0
  20. data/proctor/templates/monit_faye_s.erb +7 -0
  21. data/{lib/proctor/app_global.rb → proctor/templates/monit_web_s.erb} +0 -0
  22. data/{lib/proctor/config_global.rb → proctor/templates/upstart_default_n.erb} +0 -0
  23. data/proctor/templates/upstart_default_s.erb +7 -0
  24. data/proctor/templates/upstart_faye_s.erb +9 -0
  25. data/proctor/{managers/MonitDefault.rb → templates/upstart_passenger_s.erb} +0 -0
  26. data/proctor.gemspec +2 -0
  27. data/spec/acceptance/help_spec.rb +17 -1
  28. data/spec/acceptance/list_spec.rb +17 -0
  29. data/spec/spec_helper.rb +7 -4
  30. data/spec/support/aruba.rb +2 -2
  31. data/spec/unit/app_config_spec.rb +30 -0
  32. data/spec/unit/app_file_spec.rb +36 -5
  33. data/spec/unit/manager_spec.rb +16 -7
  34. data/spec/unit/service_spec.rb +20 -0
  35. data/spec/unit/template_config_spec.rb +12 -0
  36. data/spec/unit/template_file_spec.rb +16 -0
  37. data/spec/unit/template_spec.rb +3 -2
  38. metadata +61 -25
  39. data/lib/proctor/config_file.rb +0 -5
  40. data/lib/proctor/export.rb +0 -5
  41. data/lib/proctor/job.rb +0 -4
  42. data/proctor/managers/MonitFaye.rb +0 -0
  43. data/proctor/managers/MonitWeb.rb +0 -0
  44. data/proctor/managers/UpstartDefault.rb +0 -0
  45. data/proctor/managers/UpstartFaye.rb +0 -0
  46. data/proctor/managers/UpstartWeb.rb +0 -0
  47. data/proctor/templates/monit_default.erb +0 -0
  48. data/proctor/templates/monit_faye.erb +0 -0
  49. data/proctor/templates/monit_web.erb +0 -0
  50. data/proctor/templates/upstart_default.erb +0 -0
  51. data/proctor/templates/upstart_faye.erb +0 -0
  52. data/proctor/templates/upstart_web.erb +0 -0
  53. data/spec/unit/config_file_spec.rb +0 -9
  54. data/spec/unit/export_spec.rb +0 -9
  55. data/spec/unit/job_spec.rb +0 -9
@@ -0,0 +1,71 @@
1
+ module Proctor
2
+ module Presenter
3
+ class Show
4
+
5
+ def initialize(global, options, args)
6
+ @global = global
7
+ @options = options
8
+ @args = args
9
+ validate_handle(args[0])
10
+ end
11
+
12
+ def validate_handle(handle)
13
+ if handle.length != 3
14
+ raise "invalid handle (#{handle}) see 'proctor list for a valid handle'"
15
+ end
16
+ end
17
+
18
+ def xhandle(string)
19
+ Digest::MD5.hexdigest(string)[0..2]
20
+ end
21
+
22
+ def find_asset(handle)
23
+
24
+ result = @options[:templates].values.find {|x| xhandle(x) == handle}
25
+ return ['template', result] unless result.nil?
26
+
27
+ result = AppConfig.app_files(@global).find {|x| xhandle(x) == handle}
28
+ return ['appfile', result] unless result.nil?
29
+
30
+ result = @options[:app_config]['managers'].keys.find {|x| xhandle(x) == handle}
31
+ return ['manager', result] unless result.nil?
32
+
33
+ result = @options[:app_config]['services'].keys.find {|x| xhandle(x) == handle}
34
+ return ['service', result] unless result.nil?
35
+
36
+ raise "unknown handle (#{handle}) see 'proctor list' for a valid handle"
37
+
38
+ end
39
+
40
+ def show
41
+ type, asset = find_asset(@args[0])
42
+
43
+ case type
44
+ when "template" then show_template(asset)
45
+ when "appfile" then show_appfile(asset)
46
+ when "manager" then show_manager(asset)
47
+ when "service" then show_service(asset)
48
+ end
49
+ end
50
+
51
+ def show_template(asset)
52
+ puts "# Template #{asset}"
53
+ puts File.read(asset)
54
+ end
55
+
56
+ def show_appfile(asset)
57
+ puts "# AppFile #{asset}"
58
+ puts File.read(asset)
59
+ end
60
+
61
+ def show_manager(asset)
62
+ puts "# Manager #{asset}"
63
+ end
64
+
65
+ def show_service(asset)
66
+ puts "# Service #{asset}"
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,8 @@
1
+ module Proctor
2
+ class Service
3
+ attr_reader :input_hash, :opts
4
+ def initialize(hash = {})
5
+ @input_hash = hash
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ module Proctor
2
+ class TemplateConfig
3
+
4
+ def self.load(global_options = {})
5
+ template_files = self.files(global_options)
6
+ template_files.reduce({}) do |a, v|
7
+ a.merge({File.basename(v) => v})
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def self.files(global_options)
14
+ Dir.glob('proctor/templates/*').map {|f| File.expand_path(f)}
15
+ end
16
+
17
+ #def self.base_dir
18
+ # File.expand_path(File.dirname(File.expand_path(__FILE__)) + '/../..')
19
+ #end
20
+
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ require 'yaml'
2
+
3
+ module Proctor
4
+ class TemplateFile
5
+ attr_reader :config_data
6
+
7
+ def initialize(filename)
8
+ check_file_existence(filename)
9
+ @config_data = load_data(filename)
10
+ check_data_format(filename)
11
+ end
12
+
13
+ private
14
+
15
+ def load_data(file)
16
+ begin
17
+ @config_data = YAML.load_file(file)
18
+ rescue Psych::SyntaxError
19
+ error_msg = "invalid file format (expecting YAML - #{file})"
20
+ raise InvalidYamlException.new, error_msg
21
+ end
22
+ end
23
+
24
+ def check_data_format(file)
25
+ raise InvalidDataType, "expecting hash data (#{file})" unless @config_data.is_a?(Hash)
26
+ end
27
+
28
+ def check_file_existence(file)
29
+ error_msg = "missing app file (#{file})"
30
+ raise MissingFileException, error_msg unless File.exist?(file)
31
+ end
32
+
33
+ end
34
+
35
+ class MissingFileException < RuntimeError
36
+ end
37
+
38
+ class InvalidYamlException < RuntimeError
39
+ end
40
+
41
+ class InvalidDataType < RuntimeError
42
+ end
43
+
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Proctor
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/proctor.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  BASE_DIR = File.expand_path(File.dirname(__FILE__))
2
- Dir[BASE_DIR + '/proctor/*'].each {|f| require f}
2
+ Dir[BASE_DIR + '/proctor/**/*'].each {|f| require f unless Dir.exist?(f)}
3
3
 
4
4
  module Proctor
5
5
  end
data/proctor/Proctorfile CHANGED
@@ -1,28 +1,52 @@
1
1
  # vim: set ft=yaml:
2
2
  ---
3
- # exports:
4
- # upstart:
5
- # start_command: "asdf"
6
- # stop_command: "asdf"
7
- # monit:
8
- # reload_command: "asdf"
9
- # procs:
10
- # web:
11
- # start_command: "asdf"
12
- # stop_command: "asdf"
13
- # faye:
14
- # start_command: "asdf"
15
3
 
16
- exports:
4
+ managers:
5
+ init:
6
+ export_directory: "/etc/init.d"
17
7
  upstart:
18
- start_command: "qwer"
19
- stop_command: "qwer"
8
+ export_directory: "/etc/init"
9
+ start_command: "/sbin/init start <%= app_name %>"
10
+ stop_command: "/sbin/init stop <%= app_name %>"
11
+ status_command: "/sbin/init status <%= app_name %>"
20
12
  monit:
21
- reload_command: "qwer"
22
- procs:
23
- web:
24
- start_command: "qwer"
25
- stop_command: "qwer"
13
+ export_directory: "/etc/monit/conf.d"
14
+ reload_command: "monit reload"
15
+ stop_command: "monit quit"
16
+ start_command: "monit"
17
+ status_command: "monit summary"
18
+ pidfile: "/a/b/c"
19
+ foreman:
20
+ start_command: "foreman start"
21
+ use_master_template: true
22
+ use_worker_template: false
23
+
24
+ services:
25
+ unicorn:
26
+ start_command: "bin/unicorn --log_file shared/log/<%= name %>.log"
27
+ stop_command: "qwer"
28
+ passenger:
29
+ start_command: "bin/passenger --log_file shared/log/<%= name %>.log"
30
+ stop_command: "qwer"
26
31
  faye:
27
32
  start_command: "qwer"
33
+ export.monit.memory_limit: "<%= ForemanEnv.production? ? '20MB' : '10MB' %>"
34
+ exports_to: ['monit', 'upstart']
35
+ start_command: "script/faye_script.sh"
36
+ port: 2343
37
+
38
+ nodes:
39
+ dev:
40
+ - passenger
41
+ - faye
42
+ - postgres-standalone
43
+ - jobq
44
+ - renderpro
45
+ - redis
46
+ production:
47
+ - unicorn
48
+ - faye
49
+ - postgres-shared
50
+ backup:
51
+ - postgres
28
52
 
@@ -0,0 +1,9 @@
1
+ # Generated by Proctor at <%= Time.now %>
2
+
3
+ start on reboot
4
+ expect daemon
5
+ respawn
6
+ pre-start script
7
+ emit starting_faye
8
+ end
9
+ exec <%= @start_command %> --port <%= port %>
@@ -0,0 +1,6 @@
1
+ % mon = managers['monit']
2
+ # Generated by Proctor at <%= Time.now %>
3
+
4
+ check process <%= role_name %> with pidfile <%= pidfile %>
5
+ start program = "<%= start_command %>"
6
+ stop program = "<%= stop_command %>"
@@ -0,0 +1,7 @@
1
+ # Generated by Proctor at <%= Time.now %>
2
+
3
+ check process <%= app_name %> with pidfile <%= pidfile %>
4
+ start program "/etc/init.d/<%= app_name %> start"
5
+ stop program "/etc/init.d/<%= app_name %> stop"
6
+ if failed port <%= port %> then restart
7
+ if 5 restarts within 5 cycles then timeout
@@ -0,0 +1,7 @@
1
+ # Generated by Proctor at <%= Time.now %>
2
+
3
+ start on runlevel [2345]
4
+ stop on runlevel [!2345]
5
+ expect daemon
6
+ respawn
7
+ exec <%= @start_command %>
@@ -0,0 +1,9 @@
1
+ # Generated by Proctor at <%= Time.now %>
2
+
3
+ start on reboot
4
+ expect daemon
5
+ respawn
6
+ pre-start script
7
+ emit starting_faye
8
+ end
9
+ exec <%= @start_command %> --port <%= port %>
data/proctor.gemspec CHANGED
@@ -18,10 +18,12 @@ Gem::Specification.new do |gem|
18
18
 
19
19
  gem.add_dependency('gli')
20
20
  gem.add_dependency('foreman')
21
+ gem.add_dependency('hash-deep-merge')
21
22
 
22
23
  gem.add_development_dependency('rake')
23
24
  gem.add_development_dependency('rdoc')
24
25
  gem.add_development_dependency('rspec')
25
26
  gem.add_development_dependency('aruba')
27
+ gem.add_development_dependency('debugger')
26
28
 
27
29
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'start/help' do
3
+ describe 'start/help', :acceptance do
4
4
 
5
5
  it 'returns text' do
6
6
  start_simple
@@ -9,6 +9,7 @@ describe 'start/help' do
9
9
 
10
10
  it 'does not have an error' do
11
11
  start_simple
12
+ last_exit_status.should == 0
12
13
  all_output.should_not include('Error')
13
14
  end
14
15
 
@@ -17,4 +18,19 @@ describe 'start/help' do
17
18
  all_output.should include('start')
18
19
  end
19
20
 
21
+ end
22
+
23
+ describe "checking existence of App file" do
24
+ it "returns an error w/o the app file" do
25
+ clear_app_file do
26
+ start_simple "list", false
27
+ last_exit_status.should_not == 0
28
+ all_output.should include('error: missing app file')
29
+ end
30
+ end
31
+ it "returns an error when specifying an alternative proctorfile" do
32
+ start_simple "list -f Nofile", false
33
+ last_exit_status.should_not == 0
34
+ all_output.should include('error: missing app file')
35
+ end
20
36
  end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'list', :acceptance do
4
+
5
+ it 'returns help text' do
6
+ start_simple "help list"
7
+ all_output.should include('list')
8
+ end
9
+
10
+ it 'raises an error with an unknown type' do
11
+ start_simple "list -t whatexxver", false
12
+ last_exit_status.should_not == 0
13
+ all_output.should include('error')
14
+ end
15
+
16
+ end
17
+
data/spec/spec_helper.rb CHANGED
@@ -13,12 +13,15 @@ RSpec.configure do |config|
13
13
  config.color_enabled = true
14
14
  config.treat_symbols_as_metadata_keys_with_true_values = true
15
15
  config.run_all_when_everything_filtered = true
16
- config.filter_run :focus
17
16
  config.include Aruba::Api, :example_group => {
18
17
  :file_path => /spec\/acceptance/
19
18
  }
20
- config.before(:suite, :example_group => {
21
- :file_path => /spec\/unit/
22
- }) { $in_unit_spec_suite = true }
23
19
 
24
20
  end
21
+
22
+ def clear_app_file(&block)
23
+ system "mv Proctorfile /tmp/Proctorfile.#{$$}" if File.exist?("Proctorfile")
24
+ yield
25
+ system "rm -f Proctorfile"
26
+ system "mv /tmp/Proctorfile.#{$$} Proctorfile" if File.exist?("/tmp/Proctorfile.#{$$}")
27
+ end
@@ -3,6 +3,6 @@ def start_interactive
3
3
  run_interactive './../../bin/proctor'
4
4
  end
5
5
 
6
- def start_simple(args = "")
7
- run_simple "./../../bin/proctor " + args
6
+ def start_simple(args = "", fail_on_error = true)
7
+ run_simple("./../../bin/proctor " + args, fail_on_error)
8
8
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/proctor/app_config"
3
+
4
+ include Proctor
5
+
6
+ describe AppConfig do
7
+ it "loads data and generates a hash" do
8
+ result = AppConfig.load
9
+ result.should_not be_nil
10
+ result.should be_a(Hash)
11
+ end
12
+ it "has default values" do
13
+ app_data = "---\nmanagers:\n newstart:\n export_directory: '/tmp/test'"
14
+ clear_app_file do
15
+ File.open("Proctorfile", 'w') {|f| f.puts "#{app_data}"}
16
+ result = AppConfig.load
17
+ tst_value = result["managers"]["upstart"]["export_directory"]
18
+ tst_value.should == "/etc/init"
19
+ end
20
+ end
21
+ it "overwrites default values with local values" do
22
+ app_data = "---\nmanagers:\n upstart:\n export_directory: '/tmp/test'"
23
+ clear_app_file do
24
+ File.open("Proctorfile", 'w') {|f| f.puts "#{app_data}"}
25
+ result = AppConfig.load
26
+ tst_value = result["managers"]["upstart"]["export_directory"]
27
+ tst_value.should == "/tmp/test"
28
+ end
29
+ end
30
+ end
@@ -1,9 +1,40 @@
1
1
  require 'spec_helper'
2
2
  require_relative "../../lib/proctor/app_file"
3
3
 
4
- describe Proctor::AppFile do
5
- it "should raise an error on bad format"
6
- it "should have a list of valid object types"
7
- it "should raise warnings for invalid objects"
8
- it "should support a --no-warn field"
4
+ include Proctor
5
+
6
+ describe AppFile do
7
+
8
+ describe "#new" do
9
+ it "reports an error if the app file is missing" do
10
+ lambda { AppFile.new("Unknown") }.should raise_error(MissingFileException)
11
+ end
12
+
13
+ it "reports an error if the app file YAML has a parser error" do
14
+ file = "/tmp/test.#{$$}"
15
+ system "echo '\007\012\043' > #{file}"
16
+ lambda { AppFile.new(file) }.should raise_error(InvalidYamlException)
17
+ system "rm #{file}"
18
+ end
19
+
20
+ it "reports an error if the data isn't a hash" do
21
+ file = "/tmp/test.#{$$}"
22
+ system "echo 'hello world' > #{file}"
23
+ lambda { AppFile.new(file) }.should raise_error(InvalidDataType)
24
+ system "rm #{file}"
25
+ end
26
+
27
+ it "works ok if the file has valid data" do
28
+ file = "/tmp/test.#{$$}"
29
+ system "echo '---\na:\n b: 1' > #{file}"
30
+ lambda { AppFile.new(file) }.should_not raise_error
31
+ system "rm #{file}"
32
+ end
33
+
34
+ end
35
+
36
+ #it "should have a list of valid object types"
37
+ #it "should raise warnings for invalid objects"
38
+ #it "should handle embedded ERB tags"
39
+ #it "reports if there is an ERB rendering error"
9
40
  end
@@ -1,11 +1,20 @@
1
1
  require 'spec_helper'
2
2
  require_relative "../../lib/proctor/manager"
3
3
 
4
- describe Proctor::Manager do
5
- it "should support a standard set of commands (export, cleanup, start, stop, reload, restart, status)"
6
- it "should have a field 'support' which list valid commands"
7
- it "should know a format and an app"
8
- it "should support a default type for app"
9
- it "should know the target directory for deploying"
10
- it "should know about dependencies" # run depends on export
4
+ include Proctor
5
+
6
+ describe Manager do
7
+ before(:each) do
8
+ @obj = Manager.new
9
+ end
10
+
11
+ it "exists" do
12
+ @obj.should_not be_nil
13
+ end
14
+
15
+ it "has an input hash" do
16
+ @obj.should respond_to(:input_hash)
17
+ @obj.input_hash.should be_a(Hash)
18
+ end
19
+
11
20
  end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/proctor/service"
3
+
4
+ include Proctor
5
+
6
+ describe Service do
7
+ before(:each) do
8
+ @obj = Service.new
9
+ end
10
+
11
+ it "should exist" do
12
+ @obj.should_not be_nil
13
+ end
14
+
15
+ it "should have an input hash" do
16
+ @obj.should respond_to(:input_hash)
17
+ @obj.input_hash.should be_a(Hash)
18
+ end
19
+
20
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/proctor/template_config"
3
+
4
+ include Proctor
5
+
6
+ describe TemplateConfig do
7
+ it "loads data and generates a hash" do
8
+ result = TemplateConfig.load
9
+ result.should_not be_nil
10
+ result.should be_a(Hash)
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/proctor/template_file"
3
+
4
+ include Proctor
5
+
6
+ describe TemplateFile do
7
+
8
+ describe "#new" do
9
+
10
+ end
11
+
12
+ #it "should have a list of valid object types"
13
+ #it "should raise warnings for invalid objects"
14
+ #it "should handle embedded ERB tags"
15
+ #it "reports if there is an ERB rendering error"
16
+ end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require_relative "../../lib/proctor/template"
3
3
 
4
4
  describe Proctor::Template do
5
- it "should support template lookup"
6
- it "should return an error if the template isn't found"
5
+ #it "should support template lookup"
6
+ #it "should return an error if the template isn't found"
7
+ #it "should report if there is a template rendering error"
7
8
  end