proctor 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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