heroku-rds 0.4.0 → 0.5.0

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 (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