ors 0.0.1
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.
- data/.autotest +3 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Rakefile +3 -0
- data/bin/ors +8 -0
- data/lib/ors/command.rb +40 -0
- data/lib/ors/commands/base.rb +18 -0
- data/lib/ors/commands/console.rb +24 -0
- data/lib/ors/commands/deploy.rb +13 -0
- data/lib/ors/commands/help.rb +32 -0
- data/lib/ors/commands/logs.rb +19 -0
- data/lib/ors/commands/migrate.rb +13 -0
- data/lib/ors/commands/restart.rb +13 -0
- data/lib/ors/commands/setup.rb +18 -0
- data/lib/ors/commands/start.rb +13 -0
- data/lib/ors/commands/stop.rb +13 -0
- data/lib/ors/commands/update.rb +14 -0
- data/lib/ors/config.rb +97 -0
- data/lib/ors/core_ext.rb +53 -0
- data/lib/ors/helpers.rb +137 -0
- data/lib/ors/log_unifier.rb +61 -0
- data/lib/ors/version.rb +3 -0
- data/lib/ors.rb +8 -0
- data/ors.gemspec +26 -0
- data/spec/ors/command_spec.rb +26 -0
- data/spec/ors/commands/base_spec.rb +21 -0
- data/spec/ors/commands/console_spec.rb +13 -0
- data/spec/ors/commands/deploy_spec.rb +19 -0
- data/spec/ors/commands/help_spec.rb +14 -0
- data/spec/ors/commands/logs_spec.rb +24 -0
- data/spec/ors/config_spec.rb +99 -0
- data/spec/ors/helpers_spec.rb +68 -0
- data/spec/ors/log_unifier_spec.rb +101 -0
- data/spec/spec_helper.rb +7 -0
- metadata +152 -0
data/.autotest
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/ors
ADDED
data/lib/ors/command.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "ors/commands/base"
|
2
|
+
Dir[File.join File.dirname(__FILE__), "commands", "*.rb"].each { |c| require c }
|
3
|
+
|
4
|
+
module ORS
|
5
|
+
|
6
|
+
class Command
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
include ORS::Commands
|
11
|
+
|
12
|
+
def run args
|
13
|
+
command, *options = args
|
14
|
+
klass = command.to_s.capitalize
|
15
|
+
|
16
|
+
if available_commands.include? klass
|
17
|
+
ORS::Config.parse_options options
|
18
|
+
|
19
|
+
if ORS::Config.valid_options?
|
20
|
+
Base.run ORS::Commands.const_get(klass)
|
21
|
+
else
|
22
|
+
Base.run Help
|
23
|
+
end
|
24
|
+
else
|
25
|
+
Base.run Help
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def available_commands
|
32
|
+
ORS::Commands.constants.map {|klass| klass.to_s }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
extend ClassMethods
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ORS::Commands
|
2
|
+
class Console < Base
|
3
|
+
def execute
|
4
|
+
execute_command console_server,
|
5
|
+
%(source ~/.rvm/scripts/rvm),
|
6
|
+
%(cd #{deploy_directory}),
|
7
|
+
%(if [ -f script/rails ]; then bundle exec rails console #{environment}; else ./script/console #{environment}; fi),
|
8
|
+
:exec => true
|
9
|
+
end
|
10
|
+
|
11
|
+
def help
|
12
|
+
puts <<-END
|
13
|
+
Usage: ./ors console [environment=production] [options]
|
14
|
+
|
15
|
+
=== Description
|
16
|
+
Replaces current process and runs rails console.
|
17
|
+
|
18
|
+
=== Options
|
19
|
+
--pretend (or -p) Don't execute anything, just show me what you're going to do
|
20
|
+
--no-gateway (or -ng) Don't use a gateway (if you're inside the firewall)
|
21
|
+
END
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ORS::Commands
|
2
|
+
|
3
|
+
class Help < Base
|
4
|
+
|
5
|
+
def execute
|
6
|
+
puts <<-END
|
7
|
+
Usage: ./ors <action> [environment=production] [options]
|
8
|
+
|
9
|
+
=== Actions
|
10
|
+
help You're looking at it
|
11
|
+
console Bring up a console on the production servers
|
12
|
+
logs Show the last few log entries from the production servers
|
13
|
+
deploy Update the code, run the migrations, and restart unicorn
|
14
|
+
setup Sets up the default environment on the servers
|
15
|
+
update Updates the code on all servers
|
16
|
+
migrate Runs the migrations on the migration server
|
17
|
+
start Starts up unicorn on the app servers
|
18
|
+
stop Stops unicorn on the app servers
|
19
|
+
restart Retarts unicorn on the app servers
|
20
|
+
|
21
|
+
=== Environments
|
22
|
+
Must be one of: production demo staging
|
23
|
+
Defaults to production.
|
24
|
+
|
25
|
+
=== Options
|
26
|
+
--pretend (or -p) Don't execute anything, just show me what you're going to do (default: false)
|
27
|
+
--no-gateway (or -ng) Don't use a gateway (if you're inside the firewall) (default: true)
|
28
|
+
END
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ORS::Commands
|
2
|
+
class Logs < Base
|
3
|
+
|
4
|
+
def execute
|
5
|
+
all_logs = app_servers.map do |server|
|
6
|
+
[
|
7
|
+
server,
|
8
|
+
execute_command(server,
|
9
|
+
%(cd #{deploy_directory}),
|
10
|
+
%(tail -n #{log_lines} log/#{environment}.log),
|
11
|
+
:capture => true)
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
puts ORS::LogUnifier.new(all_logs).unify unless pretending
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ORS::Commands
|
2
|
+
|
3
|
+
class Setup < Base
|
4
|
+
|
5
|
+
def execute
|
6
|
+
info "setting up #{name} #{environment}..."
|
7
|
+
|
8
|
+
execute_in_parallel(all_servers) {|server| setup_repo server }
|
9
|
+
execute_in_parallel(ruby_servers) {|server| setup_ruby server }
|
10
|
+
|
11
|
+
execute_command migration_server, %(source ~/.rvm/scripts/rvm),
|
12
|
+
%(cd #{deploy_directory}),
|
13
|
+
%(RAILS_ENV=#{environment} rake db:create)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ORS::Commands
|
2
|
+
|
3
|
+
class Update < Base
|
4
|
+
|
5
|
+
def execute
|
6
|
+
info "updating #{name} #{environment}..."
|
7
|
+
|
8
|
+
execute_in_parallel(all_servers) {|server| update_code server }
|
9
|
+
execute_in_parallel(ruby_servers) {|server| bundle_install server }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/ors/config.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module ORS
|
2
|
+
module Config
|
3
|
+
|
4
|
+
mattr_accessor :name, :environment, :use_gateway, :pretending, :log_lines
|
5
|
+
|
6
|
+
self.environment = "production"
|
7
|
+
self.pretending = false
|
8
|
+
self.use_gateway = true
|
9
|
+
self.log_lines = 100
|
10
|
+
|
11
|
+
module ModuleMethods
|
12
|
+
|
13
|
+
def parse_options options
|
14
|
+
self.name = name_from_git
|
15
|
+
self.environment = options.shift unless options.empty? or options.first.match(/^-/)
|
16
|
+
|
17
|
+
options.each do |option|
|
18
|
+
case option
|
19
|
+
when "-p", "--pretend" then self.pretending = true
|
20
|
+
when "-ng", "--no-gateway" then self.use_gateway = false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_options?
|
26
|
+
name.to_s.size > 0 and valid_environments.include?(environment)
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid_environments
|
30
|
+
%w(production demo staging)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def name_from_git
|
36
|
+
git.config["remote.origin.url"].gsub /.*?:(.*?).git/, '\1'
|
37
|
+
end
|
38
|
+
|
39
|
+
def git
|
40
|
+
@git ||= Git.open(Dir.pwd)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
extend ModuleMethods
|
45
|
+
|
46
|
+
def gateway
|
47
|
+
"deploy-gateway"
|
48
|
+
end
|
49
|
+
|
50
|
+
def deploy_user
|
51
|
+
"deployer"
|
52
|
+
end
|
53
|
+
|
54
|
+
def repo
|
55
|
+
"ors_git"
|
56
|
+
end
|
57
|
+
|
58
|
+
def base_path
|
59
|
+
"/var/www"
|
60
|
+
end
|
61
|
+
|
62
|
+
def web_servers
|
63
|
+
%w(koala)
|
64
|
+
end
|
65
|
+
|
66
|
+
def app_servers
|
67
|
+
%w(eel jellyfish squid)
|
68
|
+
end
|
69
|
+
|
70
|
+
def migration_server
|
71
|
+
"tuna"
|
72
|
+
end
|
73
|
+
|
74
|
+
def console_server
|
75
|
+
"tuna"
|
76
|
+
end
|
77
|
+
|
78
|
+
def ruby_servers
|
79
|
+
app_servers + [migration_server]
|
80
|
+
end
|
81
|
+
|
82
|
+
def all_servers
|
83
|
+
web_servers + app_servers + [migration_server]
|
84
|
+
end
|
85
|
+
|
86
|
+
def deploy_directory
|
87
|
+
directory = File.join base_path, name
|
88
|
+
|
89
|
+
if environment == "production"
|
90
|
+
directory
|
91
|
+
else
|
92
|
+
"#{directory}_#{environment}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
data/lib/ors/core_ext.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# copied from activesupport
|
2
|
+
class Module
|
3
|
+
def mattr_reader(*syms)
|
4
|
+
syms.each do |sym|
|
5
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
6
|
+
@@#{sym} = nil unless defined? @@#{sym}
|
7
|
+
|
8
|
+
def self.#{sym}
|
9
|
+
@@#{sym}
|
10
|
+
end
|
11
|
+
EOS
|
12
|
+
|
13
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
14
|
+
def #{sym}
|
15
|
+
@@#{sym}
|
16
|
+
end
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def mattr_writer(*syms)
|
22
|
+
syms.each do |sym|
|
23
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
24
|
+
def self.#{sym}=(obj)
|
25
|
+
@@#{sym} = obj
|
26
|
+
end
|
27
|
+
EOS
|
28
|
+
|
29
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
30
|
+
def #{sym}=(obj)
|
31
|
+
@@#{sym} = obj
|
32
|
+
end
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Extends the module object with module and instance accessors for class attributes,
|
38
|
+
# just like the native attr* accessors for instance attributes.
|
39
|
+
#
|
40
|
+
# module AppConfiguration
|
41
|
+
# mattr_accessor :google_api_key
|
42
|
+
# self.google_api_key = "123456789"
|
43
|
+
#
|
44
|
+
# mattr_accessor :paypal_url
|
45
|
+
# self.paypal_url = "www.sandbox.paypal.com"
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# AppConfiguration.google_api_key = "overriding the api key!"
|
49
|
+
def mattr_accessor(*syms)
|
50
|
+
mattr_reader(*syms)
|
51
|
+
mattr_writer(*syms)
|
52
|
+
end
|
53
|
+
end
|
data/lib/ors/helpers.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
module ORS
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
include Config
|
5
|
+
|
6
|
+
def setup_repo server
|
7
|
+
info "[#{server}] installing codebase..."
|
8
|
+
|
9
|
+
execute_command server, %(cd #{base_path}),
|
10
|
+
%(rm -rf #{deploy_directory}),
|
11
|
+
%(git clone #{REPO}:#{name} #{deploy_directory}),
|
12
|
+
%(mkdir -p #{deploy_directory}/tmp/pids),
|
13
|
+
%(mkdir -p #{deploy_directory}/log)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_ruby server
|
17
|
+
info "[#{server}] installing ruby and gems..."
|
18
|
+
|
19
|
+
execute_command server, %(source ~/.rvm/scripts/rvm),
|
20
|
+
%(cd #{deploy_directory}),
|
21
|
+
%(gem install rubygems-update),
|
22
|
+
%(gem update --system),
|
23
|
+
%(gem install bundler),
|
24
|
+
%(bundle install --without development test osx > bundler.log)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_code server
|
28
|
+
info "[#{server}] updating codebase..."
|
29
|
+
|
30
|
+
execute_command server, %(cd #{deploy_directory}),
|
31
|
+
%(git fetch),
|
32
|
+
%(git checkout -q -f origin/#{environment}),
|
33
|
+
%(git reset --hard)
|
34
|
+
end
|
35
|
+
|
36
|
+
def bundle_install server
|
37
|
+
info "[#{server}] installing bundle..."
|
38
|
+
|
39
|
+
execute_command server, %(source ~/.rvm/scripts/rvm),
|
40
|
+
%(cd #{deploy_directory}),
|
41
|
+
%(bundle install --without development test osx > bundler.log)
|
42
|
+
end
|
43
|
+
|
44
|
+
def start_server server
|
45
|
+
info "[#{server}] starting unicorn..."
|
46
|
+
|
47
|
+
execute_command server, %(source ~/.rvm/scripts/rvm),
|
48
|
+
%(cd #{deploy_directory}),
|
49
|
+
%(bundle exec #{unicorn} -c config/unicorn.rb -D -E #{environment})
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop_server server
|
53
|
+
info "[#{server}] stopping unicorn..."
|
54
|
+
|
55
|
+
execute_command server, %(cd #{deploy_directory}),
|
56
|
+
%(kill \\`cat tmp/pids/unicorn.pid\\`)
|
57
|
+
end
|
58
|
+
|
59
|
+
def restart_server server
|
60
|
+
info "[#{server}] restarting unicorn..."
|
61
|
+
|
62
|
+
execute_command server, %(cd #{deploy_directory}),
|
63
|
+
%(kill -USR2 \\`cat tmp/pids/unicorn.pid\\`)
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_migrations server
|
67
|
+
info "[#{server}] running migrations..."
|
68
|
+
|
69
|
+
execute_command server, %(cd #{deploy_directory}),
|
70
|
+
%(RAILS_ENV=#{environment} rake db:migrate db:seed)
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute_in_parallel servers
|
74
|
+
servers.map do |server|
|
75
|
+
Thread.new(server) do |server|
|
76
|
+
yield server
|
77
|
+
end
|
78
|
+
end.map {|thread| thread.join }
|
79
|
+
end
|
80
|
+
|
81
|
+
# options = {:exec => ?, :capture => ?}
|
82
|
+
def execute_command server, *command_array
|
83
|
+
options = {:exec => false, :capture => false}
|
84
|
+
options.merge!(command_array.pop) if command_array.last.is_a?(Hash)
|
85
|
+
|
86
|
+
command = build_command(server, command_array, options)
|
87
|
+
|
88
|
+
if pretending
|
89
|
+
info("[#{server}] #{command}")
|
90
|
+
else
|
91
|
+
if options[:exec]
|
92
|
+
exec command
|
93
|
+
else
|
94
|
+
results = `#{command}`
|
95
|
+
if options[:capture]
|
96
|
+
return results
|
97
|
+
else
|
98
|
+
results.split("\n").each do |result|
|
99
|
+
info("[#{server}] #{result}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_command server, *commands_and_maybe_options
|
107
|
+
return "" if commands_and_maybe_options.empty?
|
108
|
+
|
109
|
+
if commands_and_maybe_options.last.is_a?(Hash)
|
110
|
+
options = commands_and_maybe_options.pop
|
111
|
+
command_array = commands_and_maybe_options
|
112
|
+
else
|
113
|
+
command_array = commands_and_maybe_options
|
114
|
+
options = {}
|
115
|
+
end
|
116
|
+
|
117
|
+
commands = command_array.join " && "
|
118
|
+
psuedo_tty = options[:exec] ? '-t ' : ''
|
119
|
+
|
120
|
+
if use_gateway
|
121
|
+
%(ssh #{psuedo_tty}#{gateway} 'ssh #{psuedo_tty}#{deploy_user}@#{server} "#{commands}"')
|
122
|
+
else
|
123
|
+
%(ssh #{psuedo_tty}#{deploy_user}@#{server} "#{commands}")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def info message
|
128
|
+
STDOUT.puts message
|
129
|
+
end
|
130
|
+
|
131
|
+
def fatal message
|
132
|
+
info message
|
133
|
+
exit 1
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ORS
|
2
|
+
|
3
|
+
class LogUnifier
|
4
|
+
|
5
|
+
attr_reader :logs, :pretty_adjust
|
6
|
+
|
7
|
+
def initialize logs
|
8
|
+
@pretty_adjust = 0
|
9
|
+
|
10
|
+
@logs = logs.inject(Hash.new) do |hash, (server, log_rows)|
|
11
|
+
@pretty_adjust = [@pretty_adjust, server.length].max
|
12
|
+
hash[server] = log_rows
|
13
|
+
hash
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def unify
|
18
|
+
group_by_entry.
|
19
|
+
select {|entry| entry[:timestamp].size == 14 }.
|
20
|
+
sort_by {|entry| entry[:timestamp] }.
|
21
|
+
map do |entry|
|
22
|
+
entry[:lines].
|
23
|
+
map {|line| ["[#{entry[:server]}]".ljust(pretty_adjust + 3), line].join }.
|
24
|
+
join "\n"
|
25
|
+
end.
|
26
|
+
flatten.
|
27
|
+
join("\n\n\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def group_by_entry
|
33
|
+
entries = Array.new
|
34
|
+
|
35
|
+
logs.each do |server, log_rows|
|
36
|
+
entry = {:lines => Array.new, :server => server}
|
37
|
+
|
38
|
+
log_rows.split(/\n/).each do |line|
|
39
|
+
if line == ""
|
40
|
+
unless entry[:lines].empty?
|
41
|
+
entries << entry
|
42
|
+
entry = {:lines => Array.new, :server => server}
|
43
|
+
end
|
44
|
+
else
|
45
|
+
if entry[:lines].empty?
|
46
|
+
entry[:timestamp] = line.gsub(/^(?:Processing|Started).*?(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*$/, '\1').gsub(/\D/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
entry[:lines] << line
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
entries << entry
|
54
|
+
end
|
55
|
+
|
56
|
+
entries
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/lib/ors/version.rb
ADDED
data/lib/ors.rb
ADDED
data/ors.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ors/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ors"
|
7
|
+
s.version = ORS::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jason Dew and John Long"]
|
10
|
+
s.email = ["jason.dew@ors.sc.gov and john.long@ors.sc.gov"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Heroku-like deployment utilities for ORS}
|
13
|
+
s.description = %q{Heroku-like deployment utilities for ORS}
|
14
|
+
|
15
|
+
s.rubyforge_project = "ors"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "git"
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "rr"
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Command do
|
4
|
+
|
5
|
+
subject { ORS::Command }
|
6
|
+
|
7
|
+
context ".run" do
|
8
|
+
|
9
|
+
it "should execute help when the command is help" do
|
10
|
+
mock(ORS::Commands::Help).new { mock!.execute.subject }
|
11
|
+
subject.run ["help"]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should execute help when no command is given" do
|
15
|
+
mock(ORS::Commands::Help).new { mock!.execute.subject }
|
16
|
+
subject.run []
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should execute help when an unknown command is given" do
|
20
|
+
mock(ORS::Commands::Help).new { mock!.execute.subject }
|
21
|
+
subject.run ["as0d9fja0s9djf"]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Commands::Base do
|
4
|
+
|
5
|
+
context ".run" do
|
6
|
+
|
7
|
+
it "should instantiate the command and call #execute on it" do
|
8
|
+
klass = mock!.new { mock!.execute.subject }.subject
|
9
|
+
ORS::Commands::Base.run klass
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#run" do
|
15
|
+
it "should call the class method" do
|
16
|
+
mock(ORS::Commands::Base).run("Foo")
|
17
|
+
subject.run "Foo"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Commands::Console do
|
4
|
+
|
5
|
+
context "#run" do
|
6
|
+
it "should set pretending to true and call exec" do
|
7
|
+
stub(subject).name {'abc/growhealthy'}
|
8
|
+
stub(subject).environment {'production'}
|
9
|
+
mock(subject).execute_command(is_a(String), is_a(String), is_a(String), is_a(String), is_a(Hash)).returns("command")
|
10
|
+
subject.execute
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Commands::Deploy do
|
4
|
+
|
5
|
+
context "#execute" do
|
6
|
+
|
7
|
+
it "should call update, migrate, then restart" do
|
8
|
+
mock(subject).info /deploying/
|
9
|
+
|
10
|
+
mock(subject).run(ORS::Commands::Update)
|
11
|
+
mock(subject).run(ORS::Commands::Migrate)
|
12
|
+
mock(subject).run(ORS::Commands::Restart)
|
13
|
+
|
14
|
+
subject.execute
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Commands::Logs do
|
4
|
+
|
5
|
+
context "#execute" do
|
6
|
+
it "should get logs from all of the app servers and then unify them" do
|
7
|
+
mock(subject).pretending { false }
|
8
|
+
mock(subject).app_servers { mock!.map { :logs }.subject }
|
9
|
+
mock(ORS::LogUnifier).new(:logs) { mock!.unify { :output }.subject }
|
10
|
+
mock(subject).puts(:output)
|
11
|
+
|
12
|
+
subject.execute
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not call the LogUnifier if pretending" do
|
16
|
+
mock(subject).pretending { true }
|
17
|
+
mock(subject).app_servers { mock!.map { :logs }.subject }
|
18
|
+
mock(ORS::LogUnifier).new(:logs).never
|
19
|
+
|
20
|
+
subject.execute
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Config do
|
4
|
+
|
5
|
+
subject { class Foo; include ORS::Config; end; Foo.new }
|
6
|
+
|
7
|
+
context ".parse_options" do
|
8
|
+
it("should default pretend to false") { subject.pretending.should be_false }
|
9
|
+
it("should default use_gateway to true") { subject.use_gateway.should be_true }
|
10
|
+
|
11
|
+
it "should set the environment when it is given" do
|
12
|
+
ORS::Config.parse_options %w(foobar -p)
|
13
|
+
subject.environment.should == "foobar"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set pretend to true if -p is given" do
|
17
|
+
ORS::Config.pretending = false
|
18
|
+
ORS::Config.parse_options %w(-p)
|
19
|
+
|
20
|
+
subject.pretending.should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should set pretend to true if --pretend is given" do
|
24
|
+
ORS::Config.pretending = false
|
25
|
+
ORS::Config.parse_options %w(--pretend)
|
26
|
+
|
27
|
+
subject.pretending.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set use_gateway to false if -ng is given" do
|
31
|
+
ORS::Config.use_gateway = true
|
32
|
+
ORS::Config.parse_options %w(-ng)
|
33
|
+
|
34
|
+
subject.use_gateway.should be_false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should set use_gateway to false if --no-gateway is given" do
|
38
|
+
ORS::Config.use_gateway = true
|
39
|
+
ORS::Config.parse_options %w(--no-gateway)
|
40
|
+
|
41
|
+
subject.use_gateway.should be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context ".valid_options?" do
|
46
|
+
|
47
|
+
it "should be true when there is a name and valid environment" do
|
48
|
+
subject.name = "foo"
|
49
|
+
subject.environment = "production"
|
50
|
+
|
51
|
+
ORS::Config.valid_options?.should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be false when there is a name but an invalid environment" do
|
55
|
+
subject.name = "foo"
|
56
|
+
subject.environment = "-p"
|
57
|
+
|
58
|
+
ORS::Config.valid_options?.should be_false
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be false when there is a valid environment but a blank name" do
|
62
|
+
subject.name = ""
|
63
|
+
subject.environment = "production"
|
64
|
+
|
65
|
+
ORS::Config.valid_options?.should be_false
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
context "#all_servers" do
|
71
|
+
it "should return all servers" do
|
72
|
+
subject.all_servers.should == (subject.web_servers + subject.app_servers + [subject.migration_server])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "config permanence" do
|
77
|
+
before do
|
78
|
+
class ORS::OtherConfig; include ORS::Config; end
|
79
|
+
@other_config = ORS::OtherConfig.new
|
80
|
+
|
81
|
+
class ORS::ConfigTest; include ORS::Config; end
|
82
|
+
@some_config = ORS::ConfigTest.new
|
83
|
+
end
|
84
|
+
|
85
|
+
%w(use_gateway pretending).each do |accessor|
|
86
|
+
it "should allow you to set #{accessor}" do
|
87
|
+
ORS::Config.should respond_to("#{accessor}")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should know if its #{accessor} across classes" do
|
91
|
+
ORS::Config.send("#{accessor}=", true)
|
92
|
+
|
93
|
+
@some_config.send(accessor).should == true
|
94
|
+
@other_config.send(accessor).should == true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ORS::Helpers do
|
4
|
+
|
5
|
+
subject { class ORS::HelperTest; include ORS::Helpers; end; ORS::HelperTest.new }
|
6
|
+
|
7
|
+
context "#execute_command" do
|
8
|
+
before do
|
9
|
+
stub(subject).info(is_a(String)).returns "message"
|
10
|
+
stub(subject).build_command.returns "command"
|
11
|
+
stub(subject).`(is_a(String)) {'output'} # ` # syntax highlighting
|
12
|
+
end
|
13
|
+
|
14
|
+
context "without options" do
|
15
|
+
it "should not fail" do
|
16
|
+
lambda {subject.execute_command("server", "command1", "command2")}.should_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not run exec" do
|
20
|
+
dont_allow(subject).exec(is_a(String))
|
21
|
+
subject.execute_command("server", "command1", "command2")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with options" do
|
26
|
+
it "should run exec with exec option" do
|
27
|
+
mock(subject).exec(is_a(String)) { "return" }
|
28
|
+
subject.execute_command("server", "command1", "command2", :exec => true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not put results into stdout when capturing" do
|
32
|
+
dont_allow(subject).info(is_a(String))
|
33
|
+
subject.execute_command("server", "command1", "command2", :capture => true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "#build_command" do
|
39
|
+
|
40
|
+
it "should return a blank string given no commands" do
|
41
|
+
subject.build_command("server").should == ""
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with gateway" do
|
45
|
+
before { mock(subject).use_gateway.returns(true) }
|
46
|
+
|
47
|
+
it "should build the command" do
|
48
|
+
subject.build_command("server", %(cd /tmp)).should == %(ssh deploy-gateway 'ssh deployer@server "cd /tmp"')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should build the command with psuedo tty" do
|
52
|
+
subject.build_command("server", %(cd /tmp), :exec => true).should == %(ssh -t deploy-gateway 'ssh -t deployer@server "cd /tmp"')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "without gateway" do
|
57
|
+
before { mock(subject).use_gateway.returns(false) }
|
58
|
+
|
59
|
+
it "should build the command" do
|
60
|
+
subject.build_command("server", %(cd /tmp)).should == %(ssh deployer@server "cd /tmp")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should build the command with psuedo tty" do
|
64
|
+
subject.build_command("server", %(cd /tmp), :exec => true).should == %(ssh -t deployer@server "cd /tmp")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ORS
|
4
|
+
describe LogUnifier do
|
5
|
+
|
6
|
+
context "#unify" do
|
7
|
+
|
8
|
+
it "should return the logs if there is only one server" do
|
9
|
+
logs = <<-END
|
10
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
11
|
+
Processing by ReviewsController#index as HTML
|
12
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
13
|
+
|
14
|
+
|
15
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
16
|
+
Processing by ReviewsController#index as HTML
|
17
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
18
|
+
END
|
19
|
+
|
20
|
+
logs_with_server = <<-END
|
21
|
+
[server] Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
22
|
+
[server] Processing by ReviewsController#index as HTML
|
23
|
+
[server] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
24
|
+
|
25
|
+
|
26
|
+
[server] Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
27
|
+
[server] Processing by ReviewsController#index as HTML
|
28
|
+
[server] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
29
|
+
END
|
30
|
+
|
31
|
+
unifier = LogUnifier.new [["server", logs]]
|
32
|
+
unifier.unify.should == logs_with_server.chomp
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should intertwine log entires by date if there are multiple servers" do
|
36
|
+
server_1_logs = <<-END
|
37
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
38
|
+
|
39
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
40
|
+
Processing by ReviewsController#index as HTML
|
41
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
42
|
+
|
43
|
+
|
44
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:58:33 -0500
|
45
|
+
Processing by ReviewsController#index as HTML
|
46
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
47
|
+
END
|
48
|
+
|
49
|
+
server_2_logs = <<-END
|
50
|
+
Processing by ReviewsController#index as HTML
|
51
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
52
|
+
|
53
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:49:33 -0500
|
54
|
+
Processing by ReviewsController#index as HTML
|
55
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
56
|
+
|
57
|
+
|
58
|
+
Started GET "/" for 10.203.228.96 at 2011-02-02 08:52:33 -0500
|
59
|
+
Processing by ReviewsController#index as HTML
|
60
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
61
|
+
|
62
|
+
|
63
|
+
Started GET "/" for 10.203.228.96 at 2011-02-03 05:00:33 -0500
|
64
|
+
Processing by ReviewsController#index as HTML
|
65
|
+
Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
66
|
+
END
|
67
|
+
|
68
|
+
answer = <<-END
|
69
|
+
[server1] Started GET "/" for 10.203.228.96 at 2011-02-02 08:48:33 -0500
|
70
|
+
[server1] Processing by ReviewsController#index as HTML
|
71
|
+
[server1] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
72
|
+
|
73
|
+
|
74
|
+
[server2] Started GET "/" for 10.203.228.96 at 2011-02-02 08:49:33 -0500
|
75
|
+
[server2] Processing by ReviewsController#index as HTML
|
76
|
+
[server2] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
77
|
+
|
78
|
+
|
79
|
+
[server2] Started GET "/" for 10.203.228.96 at 2011-02-02 08:52:33 -0500
|
80
|
+
[server2] Processing by ReviewsController#index as HTML
|
81
|
+
[server2] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
82
|
+
|
83
|
+
|
84
|
+
[server1] Started GET "/" for 10.203.228.96 at 2011-02-02 08:58:33 -0500
|
85
|
+
[server1] Processing by ReviewsController#index as HTML
|
86
|
+
[server1] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
87
|
+
|
88
|
+
|
89
|
+
[server2] Started GET "/" for 10.203.228.96 at 2011-02-03 05:00:33 -0500
|
90
|
+
[server2] Processing by ReviewsController#index as HTML
|
91
|
+
[server2] Completed 200 OK in 41ms (Views: 3.6ms | ActiveRecord: 31.6ms)
|
92
|
+
END
|
93
|
+
|
94
|
+
unifier = LogUnifier.new([["server1", server_1_logs], ["server2", server_2_logs]])
|
95
|
+
unifier.unify.should == answer.chomp
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ors
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Dew and John Long
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-03 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: git
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rr
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Heroku-like deployment utilities for ORS
|
64
|
+
email:
|
65
|
+
- jason.dew@ors.sc.gov and john.long@ors.sc.gov
|
66
|
+
executables:
|
67
|
+
- ors
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
extra_rdoc_files: []
|
71
|
+
|
72
|
+
files:
|
73
|
+
- .autotest
|
74
|
+
- .gitignore
|
75
|
+
- .rspec
|
76
|
+
- Gemfile
|
77
|
+
- Rakefile
|
78
|
+
- bin/ors
|
79
|
+
- lib/ors.rb
|
80
|
+
- lib/ors/command.rb
|
81
|
+
- lib/ors/commands/base.rb
|
82
|
+
- lib/ors/commands/console.rb
|
83
|
+
- lib/ors/commands/deploy.rb
|
84
|
+
- lib/ors/commands/help.rb
|
85
|
+
- lib/ors/commands/logs.rb
|
86
|
+
- lib/ors/commands/migrate.rb
|
87
|
+
- lib/ors/commands/restart.rb
|
88
|
+
- lib/ors/commands/setup.rb
|
89
|
+
- lib/ors/commands/start.rb
|
90
|
+
- lib/ors/commands/stop.rb
|
91
|
+
- lib/ors/commands/update.rb
|
92
|
+
- lib/ors/config.rb
|
93
|
+
- lib/ors/core_ext.rb
|
94
|
+
- lib/ors/helpers.rb
|
95
|
+
- lib/ors/log_unifier.rb
|
96
|
+
- lib/ors/version.rb
|
97
|
+
- ors.gemspec
|
98
|
+
- spec/ors/command_spec.rb
|
99
|
+
- spec/ors/commands/base_spec.rb
|
100
|
+
- spec/ors/commands/console_spec.rb
|
101
|
+
- spec/ors/commands/deploy_spec.rb
|
102
|
+
- spec/ors/commands/help_spec.rb
|
103
|
+
- spec/ors/commands/logs_spec.rb
|
104
|
+
- spec/ors/config_spec.rb
|
105
|
+
- spec/ors/helpers_spec.rb
|
106
|
+
- spec/ors/log_unifier_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
has_rdoc: true
|
109
|
+
homepage: ""
|
110
|
+
licenses: []
|
111
|
+
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
requirements: []
|
136
|
+
|
137
|
+
rubyforge_project: ors
|
138
|
+
rubygems_version: 1.5.0
|
139
|
+
signing_key:
|
140
|
+
specification_version: 3
|
141
|
+
summary: Heroku-like deployment utilities for ORS
|
142
|
+
test_files:
|
143
|
+
- spec/ors/command_spec.rb
|
144
|
+
- spec/ors/commands/base_spec.rb
|
145
|
+
- spec/ors/commands/console_spec.rb
|
146
|
+
- spec/ors/commands/deploy_spec.rb
|
147
|
+
- spec/ors/commands/help_spec.rb
|
148
|
+
- spec/ors/commands/logs_spec.rb
|
149
|
+
- spec/ors/config_spec.rb
|
150
|
+
- spec/ors/helpers_spec.rb
|
151
|
+
- spec/ors/log_unifier_spec.rb
|
152
|
+
- spec/spec_helper.rb
|