heroku-rds 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/.gemspec +1 -1
  2. data/README.md +10 -12
  3. data/lib/heroku/commands/rds.rb +89 -19
  4. metadata +3 -3
data/.gemspec CHANGED
@@ -3,7 +3,7 @@ require 'rubygems' unless Object.const_defined?(:Gem)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "heroku-rds"
6
- s.version = '0.4.0'
6
+ s.version = '0.5.0'
7
7
  s.authors = ["Jonathan Dance"]
8
8
  s.email = "rubygems@wuputah.com"
9
9
  s.homepage = "http://github.com/wegowise/heroku-rds"
data/README.md CHANGED
@@ -27,14 +27,17 @@ plugins:install`.
27
27
 
28
28
  ### Optional Packages
29
29
 
30
- Commands involving data transfer support a progress bar using `pv`.
31
- Install `pv` to see the awesome. Most package managers have a pv
32
- package:
30
+ * Commands involving data transfer support a progress bar using `pv`.
31
+ Install `pv` to see the awesome. Most package managers have a pv
32
+ package:
33
33
 
34
34
  brew install pv # OS X
35
35
  apt-get install pv # linux/fink
36
36
  port install pv # BSD/macports
37
37
 
38
+ * `rds:ingress` can use hirb to format tabular data. `gem install hirb`
39
+ to install it.
40
+
38
41
  ## Usage
39
42
 
40
43
  Access the command list at any time by typing `heroku help rds`:
@@ -48,19 +51,14 @@ Access the command list at any time by typing `heroku help rds`:
48
51
 
49
52
  rds:access # displays current ingress access settings
50
53
  rds:dump [FILE] # Download a database dump, bzipped and saved locally
54
+ rds:import FILE # uploads a local database dump into the remote databse
51
55
  rds:ingress [IP] [SECURITY GROUP] # Authorize ingress access to a particular IP
52
56
  rds:pull [RAILS_ENV or DATABASE_URL] # downloads the remote database into a local database
57
+ rds:push [RAILS_ENV or DATABASE_URL] # uploads the local database into the remote database
53
58
  rds:revoke [IP] [SECURITY GROUP] # Revokes previously-granted ingress access from a particular IP
54
59
 
55
60
  ## Planned features
56
61
 
57
- ### Short term
58
-
59
- * rds:import - load a local database dump into the remote database
60
- * rds:push - export a local database into the remote database
61
-
62
- ### Lower priority
63
-
64
62
  * rds:snapshot - capture a snapshot
65
63
  * rds:restore - restore from a snapshot
66
64
  * rds:reboot - reboot instance
@@ -69,5 +67,5 @@ Access the command list at any time by typing `heroku help rds`:
69
67
 
70
68
  These commands are not ingress related so the target of the command
71
69
  cannot be inferred from DATABASE\_URL. This functionality is also
72
- readily available from the RDS dashboard, so implementing them is not a
73
- priority at this time.
70
+ readily available from the RDS dashboard, so implementing them is not
71
+ considered critical.
@@ -88,17 +88,24 @@ module Heroku::Command
88
88
  # displays current ingress access settings
89
89
  #
90
90
  def access
91
- data = rds.security_groups.all.map do |group|
92
- group.ec2_security_groups.map do |group_access|
93
- [group.id, group_access['EC2SecurityGroupName'] + ' @ ' + group_access['EC2SecurityGroupOwnerId'], group_access['Status']]
94
- end +
95
- group.ip_ranges.map do |ip_range|
96
- [group.id, ip_range['CIDRIP'], ip_range['Status']]
91
+ data = Array.new
92
+ rds.security_groups.all.each do |data, group|
93
+ group.ec2_security_groups.each do |group_access|
94
+ data << [group.id, group_access['EC2SecurityGroupName'] + ' @ ' + group_access['EC2SecurityGroupOwnerId'], group_access['Status']]
97
95
  end
98
- end.flatten(1)
99
- data.unshift ['SECURITY GROUP', 'IP RANGE / SECURITY GROUP', 'STATUS']
100
- lengths = (0..2).map { |i| data.map { |d| d[i].length }.max }
101
- puts data.map { |d| '%-*s %-*s %-*s' % [lengths[0], d[0], lengths[1], d[1], lengths[2], d[2]] }.join("\n")
96
+ group.ip_ranges.each do |ip_range|
97
+ data << [group.id, ip_range['CIDRIP'], ip_range['Status']]
98
+ end
99
+ data
100
+ end
101
+ begin
102
+ require 'hirb'
103
+ puts Hirb::Helpers::AutoTable.render(data, :headers => ['Security Group', 'IP Range/Security Group', 'Status'])
104
+ rescue LoadError
105
+ data.unshift ['SECURITY GROUP', 'IP RANGE / SECURITY GROUP', 'STATUS']
106
+ lengths = (0..2).map { |i| data.map { |d| d[i].length }.max }
107
+ puts data.map { |d| '%-*s %-*s %-*s' % [lengths[0], d[0], lengths[1], d[1], lengths[2], d[2]] }.join("\n")
108
+ end
102
109
  end
103
110
 
104
111
  # rds:pull [RAILS_ENV or DATABASE_URL]
@@ -110,8 +117,69 @@ module Heroku::Command
110
117
  # the transfer.
111
118
  #
112
119
  def pull
113
- check_dependencies('mysqldump', 'mysql')
120
+ check_dependencies('mysqldump', 'mysql', '/bin/sh')
121
+ target = parse_db_location(args.shift || 'development')
122
+
123
+ display "This will erase all data in the #{target['database'].inspect} database" +
124
+ (target['host'].empty? ? '' : " on #{target['host']}") + "!"
125
+ exit unless dangerous_prompt
126
+
127
+ copy_db_to_db(database_uri, target)
128
+ end
129
+
130
+ # rds:push [RAILS_ENV or DATABASE_URL]
131
+ #
132
+ # uploads the local database into the remote database
133
+ #
134
+ # If a RAILS_ENV or DATABASE_URL is not specified, the current development environment
135
+ # is used (as read from config/database.yml). This command will confirm before executing
136
+ # the transfer.
137
+ #
138
+ def push
139
+ check_dependencies('mysqldump', 'mysql', '/bin/sh')
140
+ source = parse_db_location(args.shift || 'development')
141
+
142
+ display "This will replace the #{app} database with the #{source['database'].inspect} database" +
143
+ (source['host'].empty? ? '' : " on #{source['host']}") + "!"
144
+ exit unless dangerous_prompt
145
+
146
+ copy_db_to_db(source, database_uri)
147
+ end
148
+
149
+ # rds:import FILE
150
+ #
151
+ # uploads a local database dump into the remote databse
152
+ #
153
+ # supports gzipped, bzipped, and uncompressed sql dumps
154
+ #
155
+ def import
156
+ check_dependencies('mysql', '/bin/sh')
157
+ source = args.shift or raise CommandFailed, "You must specify a file to import from"
158
+ File.readable?(source) or raise CommandFailed, "#{source.inspect} is not readable"
159
+ command = case source
160
+ when /\.bz2$/
161
+ check_dependencies('bzcat')
162
+ 'bzcat'
163
+ when /\.gz$/
164
+ check_dependencies('gunzip')
165
+ 'gunzip -c'
166
+ else
167
+ check_dependencies('cat')
168
+ 'cat'
169
+ end
170
+
171
+ display "This will replace the #{app} database with #{source}!"
172
+ exit unless dangerous_prompt
173
+
174
+ exec('/bin/sh', '-c',
175
+ "#{command} #{args_to_s(source)}" +
176
+ pv_pipe +
177
+ %{| mysql --compress } + args_to_s(mysql_args(database_uri)))
178
+ end
114
179
 
180
+ private
181
+
182
+ def parse_db_location(target)
115
183
  target = args.shift || 'development'
116
184
  if target =~ %r{://}
117
185
  target = uri_to_hash(validate_db(URI.parse(target)))
@@ -122,19 +190,20 @@ module Heroku::Command
122
190
  db_config.has_key?(target)
123
191
  target = validate_db(db_config[target], target)
124
192
  end
193
+ target
194
+ end
125
195
 
126
- display "This will erase all data in the #{target['database'].inspect} database" +
127
- (target['host'].empty? ? '' : " on #{target['host']}") + "!"
128
- exit unless ask("Are you sure you wish to continue? [yN] ").downcase == 'y'
196
+ def dangerous_prompt
197
+ ask("Are you sure you wish to continue? [yN] ").downcase == 'y'
198
+ end
129
199
 
200
+ def copy_db_to_db(src, dest)
130
201
  exec('/bin/sh', '-c',
131
- 'mysqldump --compress --single-transaction ' + args_to_s(mysql_args(database_uri)) +
202
+ 'mysqldump --compress --single-transaction ' + args_to_s(mysql_args(src)) + ' ' +
132
203
  pv_pipe +
133
- %{| mysql --compress } + args_to_s(mysql_args(target)))
204
+ %{| mysql --compress } + args_to_s(mysql_args(dest)))
134
205
  end
135
206
 
136
- private
137
-
138
207
  def current_ip
139
208
  # simple rack app which returns your external IP
140
209
  RestClient::Resource.new("http://ip4.heroku.com")['/'].get.strip
@@ -160,7 +229,8 @@ module Heroku::Command
160
229
  args
161
230
  end
162
231
 
163
- def args_to_s(args)
232
+ def args_to_s(*args)
233
+ args = args.flatten
164
234
  "'" + args.collect { |s| s.gsub("'", "\\'") }.join("' '") + "'"
165
235
  end
166
236
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: heroku-rds
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.0
5
+ version: 0.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jonathan Dance
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-01 00:00:00 -04:00
13
+ date: 2011-06-03 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -45,8 +45,8 @@ extra_rdoc_files:
45
45
  - README.md
46
46
  - LICENSE.txt
47
47
  files:
48
- - lib/heroku/commands/rds.rb
49
48
  - lib/heroku/rds.rb
49
+ - lib/heroku/commands/rds.rb
50
50
  - README.md
51
51
  - .gemspec
52
52
  - init.rb