hsume2-cap-taffy 1.0.0 → 1.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/History.txt +4 -0
- data/README.md +20 -4
- data/Rakefile +1 -0
- data/cap-taffy.gemspec +5 -2
- data/lib/cap-taffy.rb +1 -1
- data/lib/cap-taffy/db.rb +70 -9
- data/lib/cap-taffy/parse.rb +7 -3
- data/lib/cap-taffy/ssh.rb +18 -0
- data/spec/cap-taffy/db_spec.rb +52 -47
- data/spec/cap-taffy/ssh_spec.rb +37 -0
- data/spec/cap-taffy_spec.rb +0 -3
- data/spec/spec_helper.rb +84 -39
- metadata +15 -2
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,7 @@ Features
|
|
7
7
|
------------------------------------------------
|
8
8
|
|
9
9
|
* Adds database transfer recipes (via [`Taps`]("http://github.com/ricardochimal/taps"))
|
10
|
+
* Authorize SSH access
|
10
11
|
* Manage `database.yml` (Soon.)
|
11
12
|
|
12
13
|
[`Taps`]("http://github.com/ricardochimal/taps") is great, but having to SSH into my deployment to run the `Taps` server, as well as
|
@@ -20,11 +21,9 @@ Installation
|
|
20
21
|
|
21
22
|
gem install cap-taffy
|
22
23
|
|
23
|
-
|
24
|
+
Database Transfer
|
24
25
|
------------------------------------------------
|
25
26
|
|
26
|
-
### `Taffy`: Database Transfer
|
27
|
-
|
28
27
|
> _Dependency:_ The [`Taps`]("http://github.com/ricardochimal/taps") gem is required on any server(s) you'll be transferring databases to (`:app` role) including your development machine (where you'll be running `cap` tasks from). Run:
|
29
28
|
|
30
29
|
> gem install taps
|
@@ -33,6 +32,8 @@ Usage
|
|
33
32
|
|
34
33
|
> gem install heroku
|
35
34
|
|
35
|
+
### Usage
|
36
|
+
|
36
37
|
To start, add the following to your `Capfile`
|
37
38
|
|
38
39
|
require 'cap-taffy/db'
|
@@ -58,7 +59,22 @@ Then you can use:
|
|
58
59
|
> > ssh -N -L4321:127.0.0.1:4321 henry@load-test
|
59
60
|
> > cap db:push -s taps_port=4321 -s local=true
|
60
61
|
|
61
|
-
|
62
|
+
SSH Access
|
63
|
+
------------------------------------------------
|
64
|
+
|
65
|
+
#### Usage
|
66
|
+
|
67
|
+
Add the following to your `Capfile`
|
68
|
+
|
69
|
+
require 'cap-taffy/ssh'
|
70
|
+
|
71
|
+
Using a public key generated from `ssh-keygen` (e.g. `ssh-keygen -t rsa`), to authorize access:
|
72
|
+
|
73
|
+
cap ssh:authorize # authorizes local public key for SSH access to remote server(s)
|
74
|
+
|
75
|
+
|
76
|
+
Managing `database.yml`
|
77
|
+
------------------------------------------------
|
62
78
|
|
63
79
|
> Much needed and coming soon.
|
64
80
|
|
data/Rakefile
CHANGED
@@ -26,6 +26,7 @@ PROJ.version = CapTaffy::VERSION
|
|
26
26
|
PROJ.rubyforge.name = 'cap-taffy'
|
27
27
|
PROJ.readme_file = "README.md"
|
28
28
|
PROJ.gem.dependencies = ['heroku', 'taps', 'capistrano']
|
29
|
+
PROJ.gem.development_dependencies << ["mocha"]
|
29
30
|
PROJ.description = "Capistrano recipes for deploying databases and other common tasks."
|
30
31
|
PROJ.summary = "Capistrano recipes for deploying databases (managing database.yml, importing/exporting/transfering databases, etc.)"
|
31
32
|
PROJ.ignore_file = '.gitignore'
|
data/cap-taffy.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{cap-taffy}
|
5
|
-
s.version = "1.0.
|
5
|
+
s.version = "1.0.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Henry Hsu"]
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.description = %q{Capistrano recipes for deploying databases and other common tasks.}
|
11
11
|
s.email = %q{henry@qlane.com}
|
12
12
|
s.extra_rdoc_files = ["History.txt"]
|
13
|
-
s.files = ["History.txt", "README.md", "Rakefile", "cap-taffy.gemspec", "lib/cap-taffy.rb", "lib/cap-taffy/db.rb", "lib/cap-taffy/parse.rb", "spec/cap-taffy/db_spec.rb", "spec/cap-taffy/parse_spec.rb", "spec/cap-taffy_spec.rb", "spec/spec.opts", "spec/spec_helper.rb"]
|
13
|
+
s.files = ["History.txt", "README.md", "Rakefile", "cap-taffy.gemspec", "lib/cap-taffy.rb", "lib/cap-taffy/db.rb", "lib/cap-taffy/parse.rb", "lib/cap-taffy/ssh.rb", "spec/cap-taffy/db_spec.rb", "spec/cap-taffy/parse_spec.rb", "spec/cap-taffy/ssh_spec.rb", "spec/cap-taffy_spec.rb", "spec/spec.opts", "spec/spec_helper.rb"]
|
14
14
|
s.homepage = %q{http://by.qlane.com}
|
15
15
|
s.rdoc_options = ["--main", "README.md"]
|
16
16
|
s.require_paths = ["lib"]
|
@@ -27,16 +27,19 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_runtime_dependency(%q<taps>, [">= 0"])
|
28
28
|
s.add_runtime_dependency(%q<capistrano>, [">= 0"])
|
29
29
|
s.add_development_dependency(%q<bones>, [">= 2.5.1"])
|
30
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
30
31
|
else
|
31
32
|
s.add_dependency(%q<heroku>, [">= 0"])
|
32
33
|
s.add_dependency(%q<taps>, [">= 0"])
|
33
34
|
s.add_dependency(%q<capistrano>, [">= 0"])
|
34
35
|
s.add_dependency(%q<bones>, [">= 2.5.1"])
|
36
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
35
37
|
end
|
36
38
|
else
|
37
39
|
s.add_dependency(%q<heroku>, [">= 0"])
|
38
40
|
s.add_dependency(%q<taps>, [">= 0"])
|
39
41
|
s.add_dependency(%q<capistrano>, [">= 0"])
|
40
42
|
s.add_dependency(%q<bones>, [">= 2.5.1"])
|
43
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
41
44
|
end
|
42
45
|
end
|
data/lib/cap-taffy.rb
CHANGED
data/lib/cap-taffy/db.rb
CHANGED
@@ -6,30 +6,49 @@ rescue LoadError
|
|
6
6
|
error "Install the Taps gem to use db commands. On most systems this will be:\nsudo gem install taps"
|
7
7
|
end
|
8
8
|
|
9
|
+
require File.join(File.dirname(__FILE__), %w[.. cap-taffy]) unless defined?(CapTaffy)
|
9
10
|
require File.join(File.dirname(__FILE__), 'parse')
|
10
11
|
require 'digest/sha1'
|
11
12
|
|
12
13
|
module CapTaffy::Db
|
13
14
|
extend self
|
14
15
|
|
15
|
-
|
16
|
+
# Detects the local database url for +env+.
|
17
|
+
#
|
18
|
+
# Looks for <tt>config/database.yml</tt>.
|
19
|
+
def local_database_url(env)
|
16
20
|
return "" unless File.exists?(Dir.pwd + '/config/database.yml')
|
17
21
|
db_config = YAML.load(File.read(Dir.pwd + '/config/database.yml'))
|
18
22
|
|
19
23
|
CapTaffy::Parse.database_url(db_config, env)
|
20
24
|
end
|
21
25
|
|
22
|
-
|
26
|
+
# Detects the remote database url for +env+ and the current Capistrano +instance+.
|
27
|
+
#
|
28
|
+
# Looks for <tt>config/database.yml</tt> in the +current_path+.
|
29
|
+
def remote_database_url(instance, env)
|
23
30
|
db_yml = instance.capture "cat #{instance.current_path}/config/database.yml"
|
24
31
|
db_config = YAML::load(db_yml)
|
25
32
|
|
26
33
|
CapTaffy::Parse.database_url(db_config, env)
|
27
34
|
end
|
28
35
|
|
36
|
+
# The default server port the Taps server is started on.
|
29
37
|
def default_server_port
|
30
38
|
5000
|
31
39
|
end
|
32
40
|
|
41
|
+
# Generates the remote url used by Taps push/pull.
|
42
|
+
#
|
43
|
+
# ==== Parameters
|
44
|
+
#
|
45
|
+
# * <tt>:login, :password, :host, :port</tt> - See #run.
|
46
|
+
#
|
47
|
+
# ==== Examples
|
48
|
+
#
|
49
|
+
# login = fetch(:user)
|
50
|
+
# password = tmp_pass(login) # returns asdkf239udjhdaks (for example)
|
51
|
+
# remote_url(:login => login, :password => password, :host => 'load-test') # returns http://henry:asdkf239udjhdaks@load-test:5000
|
33
52
|
def remote_url(options={})
|
34
53
|
host = options[:host]
|
35
54
|
port = options[:port] || default_server_port
|
@@ -39,11 +58,15 @@ module CapTaffy::Db
|
|
39
58
|
url.sub(/\/$/, '')
|
40
59
|
end
|
41
60
|
|
61
|
+
# Generates a temporary password to be used for the Taps server command.
|
42
62
|
def tmp_pass(user)
|
43
63
|
Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{user}--")
|
44
64
|
end
|
45
65
|
|
46
|
-
|
66
|
+
# A quick start for a Taps client.
|
67
|
+
#
|
68
|
+
# <tt>local_database_url</tt> and <tt>remote_url</tt> refer to the options for the Taps gem (see #run).
|
69
|
+
def taps_client(local_database_url, remote_url, &blk) # :yields: client
|
47
70
|
Taps::Config.chunksize = 1000
|
48
71
|
Taps::Config.database_url = local_database_url
|
49
72
|
Taps::Config.remote_url = remote_url
|
@@ -54,7 +77,11 @@ module CapTaffy::Db
|
|
54
77
|
end
|
55
78
|
end
|
56
79
|
|
57
|
-
# server
|
80
|
+
# Generates the server command used to start a Taps server
|
81
|
+
#
|
82
|
+
# ==== Parameters
|
83
|
+
# * <tt>:remote_database_url, :login, :password</tt> - See #run.
|
84
|
+
# * <tt>:port</tt> - The +port+ the Taps server is on. If given and different from #default_server_port, appends <tt>--port=[port]</tt> to command.
|
58
85
|
def server_command(options={})
|
59
86
|
remote_database_url, login, password, port = options[:remote_database_url], options[:login], options[:password], options[:port]
|
60
87
|
port_argument = ''
|
@@ -63,7 +90,37 @@ module CapTaffy::Db
|
|
63
90
|
"taps server #{remote_database_url} #{login} #{password}#{port_argument}"
|
64
91
|
end
|
65
92
|
|
66
|
-
|
93
|
+
# The meat of the operation. Runs operations after setting up the Taps server.
|
94
|
+
#
|
95
|
+
# 1. Runs the <tt>taps</tt> taps command to start the Taps server (assuming Sinatra is running on Thin)
|
96
|
+
# 2. Wait until the server is ready
|
97
|
+
# 3. Execute block on Taps client
|
98
|
+
# 4. Close the connection(s) and bid farewell.
|
99
|
+
#
|
100
|
+
# ==== Parameters
|
101
|
+
# * <tt>:remote_database_url</tt> - Refers to local database url in the options for the Taps server command (see Taps Options).
|
102
|
+
# * <tt>:login</tt> - The login for +host+. Usually what's in <tt>set :user, "the user"</tt> in <tt>deploy.rb</tt>
|
103
|
+
# * <tt>:password</tt> - The temporary password for the Taps server.
|
104
|
+
# * <tt>:port</tt> - The +port+ the Taps server is on. If not given, defaults to #default_server_port.
|
105
|
+
# * <tt>:local_database_url</tt> - Refers to the local database url in the options for Taps client commands (see Taps Options).
|
106
|
+
#
|
107
|
+
# ==== Taps Options
|
108
|
+
#
|
109
|
+
# <tt>taps</tt>
|
110
|
+
# server <local_database_url> <login> <password> [--port=N] Start a taps database import/export server
|
111
|
+
# pull <local_database_url> <remote_url> [--chunksize=N] Pull a database from a taps server
|
112
|
+
# push <local_database_url> <remote_url> [--chunksize=N] Push a database to a taps server
|
113
|
+
#
|
114
|
+
# ==== Examples
|
115
|
+
#
|
116
|
+
# task :push do
|
117
|
+
# login = fetch(:user)
|
118
|
+
# password = Time.now.to_s
|
119
|
+
# CapTaffy.Db.run(self, { :login => login, :password => password, :remote_database_url => "sqlite://test_production", :local_database_url => "sqlite://test_development" }) do |client|
|
120
|
+
# client.cmd_send
|
121
|
+
# end
|
122
|
+
# end
|
123
|
+
def run(instance, options = {} , &blk) # :yields: client
|
67
124
|
options[:port] ||= default_server_port
|
68
125
|
remote_database_url, login, password, port, local_database_url = options[:remote_database_url], options[:login], options[:password], options[:port], options[:local_database_url]
|
69
126
|
force_local = options.delete(:local)
|
@@ -86,12 +143,16 @@ module CapTaffy::Db
|
|
86
143
|
end
|
87
144
|
end
|
88
145
|
|
89
|
-
class InvalidURL < RuntimeError
|
146
|
+
class InvalidURL < RuntimeError # :nodoc:
|
147
|
+
end
|
90
148
|
end
|
91
149
|
|
92
150
|
Capistrano::Configuration.instance.load do
|
93
151
|
namespace :db do
|
94
|
-
|
152
|
+
# Executes given block.
|
153
|
+
# If this is a dry run, any raised exceptions will be caught and +returning+ is returned.
|
154
|
+
# If this is not a dry run, any exceptions will be raised as expected.
|
155
|
+
def dry_run_safe(returning = nil, &block) # :yields:
|
95
156
|
begin
|
96
157
|
yield
|
97
158
|
rescue Exception => e
|
@@ -101,8 +162,8 @@ Capistrano::Configuration.instance.load do
|
|
101
162
|
end
|
102
163
|
|
103
164
|
task :detect, :roles => :app do
|
104
|
-
@remote_database_url = dry_run_safe('') { CapTaffy::Db.
|
105
|
-
@local_database_url = dry_run_safe('') { CapTaffy::Db.
|
165
|
+
@remote_database_url = dry_run_safe('') { CapTaffy::Db.remote_database_url(self, 'production') }
|
166
|
+
@local_database_url = dry_run_safe('') { CapTaffy::Db.local_database_url('development') }
|
106
167
|
end
|
107
168
|
|
108
169
|
desc <<-DESC
|
data/lib/cap-taffy/parse.rb
CHANGED
@@ -13,6 +13,8 @@ module CapTaffy
|
|
13
13
|
attr_accessor :instance
|
14
14
|
|
15
15
|
# Modified from :parse_database_yml in heroku/command/db.rb
|
16
|
+
#
|
17
|
+
# Accepts a complete +db_config+ hash and +env+ and parses for a database_url accordingly.
|
16
18
|
def database_url(db_config, env)
|
17
19
|
raise Invalid, "please pass me a valid Hash loaded from a database YAML file" unless db_config
|
18
20
|
conf = db_config[env]
|
@@ -33,17 +35,19 @@ module CapTaffy
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
|
38
|
+
# Override to do nothing on #new
|
39
|
+
def initialize # :nodoc:
|
37
40
|
|
38
41
|
end
|
39
42
|
|
40
|
-
#
|
43
|
+
# Override to pass-through on #escape
|
41
44
|
def escape(string)
|
42
45
|
string
|
43
46
|
end
|
44
47
|
|
45
48
|
public :uri_hash_to_url, :conf_to_uri_hash
|
46
49
|
|
47
|
-
class Invalid < RuntimeError
|
50
|
+
class Invalid < RuntimeError # :nodoc:
|
51
|
+
end
|
48
52
|
end
|
49
53
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. cap-taffy]) unless defined?(CapTaffy)
|
2
|
+
|
3
|
+
module CapTaffy::SSH
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
Capistrano::Configuration.instance.load do
|
8
|
+
namespace :ssh do
|
9
|
+
desc <<-DESC
|
10
|
+
Authorize SSH access for local computer on remote computers(s).
|
11
|
+
DESC
|
12
|
+
task :authorize do
|
13
|
+
public_key = File.read(File.expand_path(File.join(%w[~/ .ssh id_rsa.pub]))).chop
|
14
|
+
|
15
|
+
run %Q[if [ ! -f ~/.ssh/authorized_keys ]; then mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys; fi && if [ -z "$(grep "^#{public_key}$" ~/.ssh/authorized_keys)" ]; then echo "#{public_key}" >> ~/.ssh/authorized_keys && echo "Public key on '$CAPISTRANO:HOST$' authorized at '#{Time.now.to_s}'"; else echo "Public key on '$CAPISTRANO:HOST$' is already authorized."; fi]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/cap-taffy/db_spec.rb
CHANGED
@@ -22,64 +22,69 @@ module CapTaffy
|
|
22
22
|
load 'lib/cap-taffy/db.rb'
|
23
23
|
end
|
24
24
|
|
25
|
-
for_task :detect, :roles => :app, :it => "should be defined" do
|
26
|
-
@
|
27
|
-
@
|
25
|
+
for_task :detect, :roles => :app, :in => :Db, :it => "should be defined" do
|
26
|
+
@mod.expects(:remote_database_url).returns("remote_db_url")
|
27
|
+
@mod.expects(:local_database_url).returns("local_db_url")
|
28
28
|
|
29
29
|
load 'lib/cap-taffy/db.rb'
|
30
30
|
|
31
|
-
@
|
32
|
-
@
|
31
|
+
@namespace.instance_variable_get(:@remote_database_url).should == "remote_db_url"
|
32
|
+
@namespace.instance_variable_get(:@local_database_url).should == "local_db_url"
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
def load_taffy_db # :nodoc:
|
36
|
+
with_logger do
|
37
|
+
load 'lib/cap-taffy/db.rb'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should send taps client cmd_send" do
|
36
42
|
options = {:remote_database_url => "remote", :local_database_url => "local", :port => nil, :login => "a_user", :password => "a_pass"}
|
37
|
-
@
|
43
|
+
@namespace.expects(:detect)
|
38
44
|
namespace_with_variables(:taps_port => nil)
|
39
|
-
|
40
|
-
|
41
|
-
@
|
42
|
-
@db_mod.expects(:run).with(@namespace_db, options).yields(taps_client_who(:expects, :cmd_send))
|
45
|
+
db_with_expected_options(options)
|
46
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
47
|
+
@mod.expects(:run).with(@namespace, options).yields(taps_client_who(:expects, :cmd_send))
|
43
48
|
|
44
|
-
|
49
|
+
load_taffy_db
|
45
50
|
end
|
46
51
|
|
47
|
-
for_task :push, :roles => :app, :it => "should use cli argument for port" do
|
52
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should use cli argument for port" do
|
48
53
|
options = {:remote_database_url => "remote", :local_database_url => "local", :port => 1234, :login => "a_user", :password => "a_pass"}
|
49
|
-
@
|
54
|
+
@namespace.expects(:detect)
|
50
55
|
namespace_with_variables(:taps_port => 1234)
|
51
|
-
|
52
|
-
@
|
53
|
-
@
|
56
|
+
db_with_expected_options(options)
|
57
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
58
|
+
@mod.expects(:run).with(@namespace, options)
|
54
59
|
|
55
|
-
|
60
|
+
load_taffy_db
|
56
61
|
end
|
57
62
|
|
58
|
-
for_task :push, :roles => :app, :it => "should force 127.0.0.1 (local) for ssh local forwarding" do
|
63
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should force 127.0.0.1 (local) for ssh local forwarding" do
|
59
64
|
options = {:remote_database_url => "remote", :local_database_url => "local", :port => 1234, :login => "a_user", :password => "a_pass"}
|
60
|
-
@
|
65
|
+
@namespace.expects(:detect)
|
61
66
|
namespace_with_variables(:taps_port => 1234, :local => true)
|
62
|
-
|
63
|
-
@
|
64
|
-
@
|
67
|
+
db_with_expected_options(options)
|
68
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
69
|
+
@mod.expects(:run).with(@namespace, options.merge(:local => true))
|
65
70
|
|
66
|
-
|
71
|
+
load_taffy_db
|
67
72
|
end
|
68
73
|
|
69
|
-
for_task :pull, :roles => :app, :it => "should send taps client cmd_receive" do
|
74
|
+
for_task :pull, :roles => :app, :in => :Db, :it => "should send taps client cmd_receive" do
|
70
75
|
options = {:remote_database_url => "remote", :local_database_url => "local", :port => nil, :login => "a_user", :password => "a_pass"}
|
71
|
-
@
|
76
|
+
@namespace.expects(:detect)
|
72
77
|
namespace_with_variables(:taps_port => nil)
|
73
|
-
|
78
|
+
db_with_expected_options(options)
|
79
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
80
|
+
@mod.expects(:run).with(@namespace, options).yields(taps_client_who(:expects, :cmd_receive))
|
74
81
|
|
75
|
-
|
76
|
-
@db_mod.expects(:run).with(@namespace_db, options).yields(taps_client_who(:expects, :cmd_receive))
|
77
|
-
|
78
|
-
load_taffy
|
82
|
+
load_taffy_db
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
86
|
context "after capistrano" do
|
87
|
+
include CapistranoHelpers
|
83
88
|
include TaffyHelpers
|
84
89
|
|
85
90
|
before do
|
@@ -107,7 +112,7 @@ module CapTaffy
|
|
107
112
|
env = 'test'
|
108
113
|
Parse.expects(:database_url).with(@conf, env)
|
109
114
|
|
110
|
-
Db.
|
115
|
+
Db.local_database_url(env)
|
111
116
|
end
|
112
117
|
|
113
118
|
it "should detect remote database url" do
|
@@ -118,7 +123,7 @@ module CapTaffy
|
|
118
123
|
env = 'test'
|
119
124
|
Parse.expects(:database_url).with(@conf, env)
|
120
125
|
|
121
|
-
Db.
|
126
|
+
Db.remote_database_url(instance, env)
|
122
127
|
end
|
123
128
|
|
124
129
|
it "should create temporary password from time and user" do
|
@@ -174,12 +179,12 @@ module CapTaffy
|
|
174
179
|
parser = mock()
|
175
180
|
Parse.expects(:new).at_least_once.returns(parser)
|
176
181
|
parser.expects(:uri_hash_to_url).
|
177
|
-
with('username' => login, 'password' => password, 'host' => host, 'scheme' => 'http', 'path' => '')
|
182
|
+
with('username' => login, 'password' => password, 'host' => host, 'scheme' => 'http', 'path' => '')
|
178
183
|
end
|
179
184
|
|
180
185
|
it "should build remote url (with some help)" do
|
181
186
|
@options[:host] = "127.0.0.1"
|
182
|
-
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{Db.default_server_port}")
|
187
|
+
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{Db.default_server_port}").returns("remote_url/")
|
183
188
|
|
184
189
|
Db.remote_url(@options)
|
185
190
|
end
|
@@ -187,7 +192,7 @@ module CapTaffy
|
|
187
192
|
it "should build remote url with different port" do
|
188
193
|
@options[:host] = "127.0.0.1"
|
189
194
|
@options[:port] = 1234
|
190
|
-
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{@options[:port]}")
|
195
|
+
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{@options[:port]}").returns("remote_url")
|
191
196
|
|
192
197
|
Db.remote_url(@options)
|
193
198
|
end
|
@@ -199,15 +204,15 @@ module CapTaffy
|
|
199
204
|
Db.remote_url(@options).should == "remote_url"
|
200
205
|
end
|
201
206
|
|
202
|
-
|
203
|
-
|
207
|
+
running_db_it "should run with capistrano" do
|
208
|
+
capistrano_run_with(Db.server_command(@options))
|
204
209
|
|
205
210
|
Db.run(@capistrano, @options)
|
206
211
|
end
|
207
212
|
|
208
|
-
|
213
|
+
running_db_it "should do something to taps client" do
|
209
214
|
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
210
|
-
|
215
|
+
capistrano_run_with(Db.server_command(@options))
|
211
216
|
end
|
212
217
|
channel.expects(:close)
|
213
218
|
|
@@ -220,11 +225,11 @@ module CapTaffy
|
|
220
225
|
channel[:status].should == 0
|
221
226
|
end
|
222
227
|
|
223
|
-
|
228
|
+
running_db_it "should run taffy on different port" do
|
224
229
|
@options[:port] = 1234
|
225
230
|
|
226
231
|
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:1234, CTRL+C to stop\r\n" do
|
227
|
-
|
232
|
+
capistrano_run_with(Db.server_command(@options))
|
228
233
|
end
|
229
234
|
channel.expects(:close)
|
230
235
|
Db.expects(:remote_url).with(@options.merge(:host => channel[:host])).returns("remote_url")
|
@@ -237,9 +242,9 @@ module CapTaffy
|
|
237
242
|
channel[:status].should == 0
|
238
243
|
end
|
239
244
|
|
240
|
-
|
245
|
+
running_db_it "should not do anything until taps sinatra server is running" do
|
241
246
|
simulating_run_loop_with :data => "asdfasdf" do
|
242
|
-
|
247
|
+
capistrano_run_with(Db.server_command(@options))
|
243
248
|
end
|
244
249
|
|
245
250
|
client = mock()
|
@@ -250,7 +255,7 @@ module CapTaffy
|
|
250
255
|
end
|
251
256
|
|
252
257
|
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
253
|
-
|
258
|
+
capistrano_run_with(Db.server_command(@options))
|
254
259
|
end
|
255
260
|
channel.expects(:close)
|
256
261
|
Db.expects(:remote_url).with(@options.merge(:host => channel[:host], :port => 5000)).returns("remote_url")
|
@@ -263,9 +268,9 @@ module CapTaffy
|
|
263
268
|
channel[:status].should == 0
|
264
269
|
end
|
265
270
|
|
266
|
-
|
271
|
+
running_db_it "should force 127.0.0.1 (local) for remote url" do
|
267
272
|
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
268
|
-
|
273
|
+
capistrano_run_with(Db.server_command(@options))
|
269
274
|
end
|
270
275
|
channel.expects(:close)
|
271
276
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
|
3
|
+
module CapTaffy
|
4
|
+
describe 'SSH' do
|
5
|
+
include CapistranoHelpers
|
6
|
+
|
7
|
+
before do
|
8
|
+
CapTaffy.send(:remove_const, "SSH") rescue nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should load in capistrano configuration instance" do;
|
12
|
+
Capistrano::Configuration.instance.expects(:load)
|
13
|
+
|
14
|
+
load 'lib/cap-taffy/ssh.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should define :db namespace" do
|
18
|
+
Capistrano::Configuration.instance.expects(:namespace).with(:ssh)
|
19
|
+
|
20
|
+
load 'lib/cap-taffy/ssh.rb'
|
21
|
+
end
|
22
|
+
|
23
|
+
def simulating_line(line, &blk)
|
24
|
+
blk.call.then.yields(line)
|
25
|
+
line
|
26
|
+
end
|
27
|
+
|
28
|
+
for_task :authorize, :in => :SSH, :it => "should authorize on each server" do
|
29
|
+
public_key = "ssh-key2\n"
|
30
|
+
File.expects(:read).with(File.expand_path(File.join(%w[~/ .ssh id_rsa.pub]))).returns(public_key)
|
31
|
+
|
32
|
+
run_with(@namespace, anything) # Don't rly wna test bash scripts
|
33
|
+
|
34
|
+
load 'lib/cap-taffy/ssh.rb'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/cap-taffy_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib cap-taffy]))
|
2
2
|
|
3
|
-
module Capistrano
|
3
|
+
module Capistrano # :nodoc:
|
4
4
|
end
|
5
5
|
|
6
6
|
Spec::Runner.configure do |config|
|
@@ -34,12 +34,19 @@ Capistrano::Configuration.class_eval do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
class String # :nodoc:
|
38
|
+
def demodulize
|
39
|
+
gsub(/^.*::/, '')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
37
43
|
module CapistranoHelpers
|
38
|
-
def self.included(base)
|
44
|
+
def self.included(base) # :nodoc:
|
39
45
|
base.extend CapistranoHelpers::ClassMethods
|
40
46
|
end
|
41
47
|
|
42
|
-
|
48
|
+
# Stubs the Capistrano Logger and yields to the block
|
49
|
+
def with_logger(&blk) # :yields:
|
43
50
|
logger_class = Class.new
|
44
51
|
logger = mock()
|
45
52
|
logger.stub_everything
|
@@ -50,41 +57,83 @@ module CapistranoHelpers
|
|
50
57
|
Capistrano.send(:remove_const, "Logger") rescue nil
|
51
58
|
end
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
# Helper for common operations in db tasks
|
61
|
+
def db_with_expected_options(options) # :nodoc:
|
62
|
+
@namespace.stubs(:fetch).with(:user).returns(options[:login])
|
63
|
+
@namespace.instance_variable_set(:@remote_database_url, options[:remote_database_url])
|
64
|
+
@namespace.instance_variable_set(:@local_database_url, options[:local_database_url])
|
57
65
|
end
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
@
|
62
|
-
@namespace_db.instance_variable_set(:@local_database_url, options[:local_database_url])
|
67
|
+
# Stubs the variables hash used by Capistrano to accept command-line parameters
|
68
|
+
def namespace_with_variables(variables) # :nodoc:
|
69
|
+
@namespace.stubs(:variables).returns(variables)
|
63
70
|
end
|
64
71
|
|
65
|
-
|
66
|
-
|
72
|
+
# Creates an expectation for the Capistrano namespace/task <tt>instance</tt> for the <tt>:run</tt> action with <tt>*args</tt>.
|
73
|
+
def run_with(instance, *args)
|
74
|
+
instance.expects(:run).with(*args)
|
67
75
|
end
|
68
76
|
|
77
|
+
# The Capistrano <tt>:run</tt> action loops with <tt>channel</tt>, <tt>stream</tt>, and <tt>data</tt> until the channel is closed.
|
78
|
+
#
|
79
|
+
# Passing in a <tt>:run</tt> expectation, modifies the expectation such that each subsequent invocation ("loop") yields <tt>channel</tt>, <tt>stream</tt>, and <tt>data</tt>
|
80
|
+
#
|
81
|
+
# ==== Parameters
|
82
|
+
#
|
83
|
+
# * <tt>:channel</tt> - A hash containing <tt>:host</tt>.
|
84
|
+
# * <tt>:stream</tt> - A stream object.
|
85
|
+
# * <tt>:data</tt> - A data object, usually a String.
|
86
|
+
def simulating_run_loop_with(options={}, &blk) # :yields:
|
87
|
+
channel = options[:channel] || {:host => "192.168.1.20"}
|
88
|
+
stream = options[:stream]
|
89
|
+
data = options[:data]
|
90
|
+
|
91
|
+
blk.call.then.yields(channel, stream, data)
|
92
|
+
[channel, stream, data]
|
93
|
+
end
|
69
94
|
|
70
95
|
module ClassMethods
|
71
|
-
|
72
|
-
|
96
|
+
# Used in specs to test Capistrano tasks.
|
97
|
+
#
|
98
|
+
#
|
99
|
+
# Code defined in the task will be executed automatically on load. (Note the <tt>yields</tt>, see Mocha#yields[http://mocha.rubyforge.org/classes/Mocha/Expectation.html#M000043])
|
100
|
+
#
|
101
|
+
# ==== Parameters
|
102
|
+
#
|
103
|
+
# * <tt>:it</tt> - The description for the current example group.
|
104
|
+
# * <tt>:in</tt> - Specifies the module under test (as well as the namespace the task is defined in). The namespace will be deduced with the downcase of <tt>:in</tt>
|
105
|
+
#
|
106
|
+
# ==== Examples
|
107
|
+
#
|
108
|
+
# for_task :detect, :roles => :app, :in => :Somewhere, :it => "should so something" do
|
109
|
+
# @namespace # refers to the current task under test (in Capistrano tasks are executed on Capistrano::Namespaces::Namespace instances)
|
110
|
+
# @mod # refers to module CapTaffy::Somewhere
|
111
|
+
# end
|
112
|
+
def for_task(task_name, options, &block)
|
113
|
+
description = options.delete(:it)
|
114
|
+
namespace = options.delete(:in)
|
115
|
+
|
73
116
|
context ":#{task_name.to_s} task" do
|
74
117
|
before do
|
75
|
-
@
|
76
|
-
@
|
77
|
-
@
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
118
|
+
@namespace = Capistrano::Configuration.instance.namespaces[namespace.to_s.downcase.to_sym] = mock()
|
119
|
+
@namespace.stubs(:desc)
|
120
|
+
@namespace.stubs(:task)
|
121
|
+
args = [task_name, options]
|
122
|
+
unless options.empty?
|
123
|
+
@namespace.expects(:task).with(*args).yields
|
124
|
+
else
|
125
|
+
@namespace.expects(:task).with(task_name).yields
|
126
|
+
end
|
127
|
+
|
128
|
+
@mod = Module.new # The module under test
|
129
|
+
CapTaffy.const_set(namespace, @mod)
|
82
130
|
end
|
83
131
|
|
84
|
-
it
|
132
|
+
it description, &block
|
85
133
|
|
86
134
|
after do
|
87
|
-
|
135
|
+
const_name = @mod.to_s.demodulize
|
136
|
+
CapTaffy.send(:remove_const, const_name)
|
88
137
|
end
|
89
138
|
end
|
90
139
|
end
|
@@ -92,10 +141,14 @@ module CapistranoHelpers
|
|
92
141
|
end
|
93
142
|
|
94
143
|
module TaffyHelpers
|
95
|
-
def self.included(base)
|
144
|
+
def self.included(base) # :nodoc:
|
96
145
|
base.extend TaffyHelpers::ClassMethods
|
97
146
|
end
|
98
147
|
|
148
|
+
# A simple helper for mocking a quick object
|
149
|
+
#
|
150
|
+
# Usage:
|
151
|
+
# taps_client_who(:expects, :do_something)
|
99
152
|
def taps_client_who(method_symbol, *args)
|
100
153
|
client = mock()
|
101
154
|
client.send(method_symbol, *args)
|
@@ -103,24 +156,16 @@ module TaffyHelpers
|
|
103
156
|
end
|
104
157
|
|
105
158
|
module ClassMethods
|
106
|
-
|
107
|
-
|
159
|
+
# A wrapper for running CapTaffy::Db::run
|
160
|
+
def running_db_it(message, &blk)
|
161
|
+
context "when running db" do
|
108
162
|
before do
|
109
163
|
@capistrano = mock()
|
110
164
|
end
|
111
165
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# invokes one loop of block, passing in channel, stream, data as arguments
|
117
|
-
def simulating_run_loop_with(options={}, &blk)
|
118
|
-
channel = options[:channel] || {:host => "192.168.1.20"}
|
119
|
-
stream = options[:stream]
|
120
|
-
data = options[:data]
|
121
|
-
|
122
|
-
blk.call.then.yields(channel, stream, data)
|
123
|
-
[channel, stream, data]
|
166
|
+
# See CapistranoHelpers
|
167
|
+
def capistrano_run_with(*args)
|
168
|
+
run_with(@capistrano, *args)
|
124
169
|
end
|
125
170
|
|
126
171
|
it message, &blk
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hsume2-cap-taffy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henry Hsu
|
@@ -52,6 +52,16 @@ dependencies:
|
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 2.5.1
|
54
54
|
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mocha
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
55
65
|
description: Capistrano recipes for deploying databases and other common tasks.
|
56
66
|
email: henry@qlane.com
|
57
67
|
executables: []
|
@@ -68,13 +78,16 @@ files:
|
|
68
78
|
- lib/cap-taffy.rb
|
69
79
|
- lib/cap-taffy/db.rb
|
70
80
|
- lib/cap-taffy/parse.rb
|
81
|
+
- lib/cap-taffy/ssh.rb
|
71
82
|
- spec/cap-taffy/db_spec.rb
|
72
83
|
- spec/cap-taffy/parse_spec.rb
|
84
|
+
- spec/cap-taffy/ssh_spec.rb
|
73
85
|
- spec/cap-taffy_spec.rb
|
74
86
|
- spec/spec.opts
|
75
87
|
- spec/spec_helper.rb
|
76
88
|
has_rdoc: false
|
77
89
|
homepage: http://by.qlane.com
|
90
|
+
licenses:
|
78
91
|
post_install_message:
|
79
92
|
rdoc_options:
|
80
93
|
- --main
|
@@ -96,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
109
|
requirements: []
|
97
110
|
|
98
111
|
rubyforge_project: cap-taffy
|
99
|
-
rubygems_version: 1.
|
112
|
+
rubygems_version: 1.3.5
|
100
113
|
signing_key:
|
101
114
|
specification_version: 3
|
102
115
|
summary: Capistrano recipes for deploying databases (managing database.yml, importing/exporting/transfering databases, etc.)
|