pogo 2.31.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/README.md +73 -0
  2. data/bin/pogo +22 -0
  3. data/data/cacert.pem +3988 -0
  4. data/lib/heroku.rb +22 -0
  5. data/lib/heroku/auth.rb +320 -0
  6. data/lib/heroku/cli.rb +38 -0
  7. data/lib/heroku/client.rb +764 -0
  8. data/lib/heroku/client/heroku_postgresql.rb +111 -0
  9. data/lib/heroku/client/pgbackups.rb +113 -0
  10. data/lib/heroku/client/rendezvous.rb +105 -0
  11. data/lib/heroku/client/ssl_endpoint.rb +25 -0
  12. data/lib/heroku/command.rb +273 -0
  13. data/lib/heroku/command/account.rb +23 -0
  14. data/lib/heroku/command/accounts.rb +34 -0
  15. data/lib/heroku/command/addons.rb +305 -0
  16. data/lib/heroku/command/apps.rb +311 -0
  17. data/lib/heroku/command/auth.rb +86 -0
  18. data/lib/heroku/command/base.rb +230 -0
  19. data/lib/heroku/command/certs.rb +148 -0
  20. data/lib/heroku/command/config.rb +137 -0
  21. data/lib/heroku/command/db.rb +218 -0
  22. data/lib/heroku/command/domains.rb +85 -0
  23. data/lib/heroku/command/drains.rb +46 -0
  24. data/lib/heroku/command/git.rb +65 -0
  25. data/lib/heroku/command/help.rb +163 -0
  26. data/lib/heroku/command/keys.rb +115 -0
  27. data/lib/heroku/command/labs.rb +161 -0
  28. data/lib/heroku/command/logs.rb +98 -0
  29. data/lib/heroku/command/maintenance.rb +61 -0
  30. data/lib/heroku/command/pg.rb +277 -0
  31. data/lib/heroku/command/pgbackups.rb +289 -0
  32. data/lib/heroku/command/plugins.rb +110 -0
  33. data/lib/heroku/command/ps.rb +232 -0
  34. data/lib/heroku/command/releases.rb +124 -0
  35. data/lib/heroku/command/run.rb +179 -0
  36. data/lib/heroku/command/sharing.rb +89 -0
  37. data/lib/heroku/command/ssl.rb +61 -0
  38. data/lib/heroku/command/stack.rb +62 -0
  39. data/lib/heroku/command/status.rb +51 -0
  40. data/lib/heroku/command/update.rb +47 -0
  41. data/lib/heroku/command/version.rb +23 -0
  42. data/lib/heroku/deprecated.rb +5 -0
  43. data/lib/heroku/deprecated/help.rb +38 -0
  44. data/lib/heroku/distribution.rb +9 -0
  45. data/lib/heroku/helpers.rb +517 -0
  46. data/lib/heroku/helpers/heroku_postgresql.rb +104 -0
  47. data/lib/heroku/plugin.rb +161 -0
  48. data/lib/heroku/updater.rb +158 -0
  49. data/lib/heroku/version.rb +3 -0
  50. data/lib/vendor/heroku/okjson.rb +598 -0
  51. data/spec/helper/legacy_help.rb +16 -0
  52. data/spec/heroku/auth_spec.rb +246 -0
  53. data/spec/heroku/client/heroku_postgresql_spec.rb +34 -0
  54. data/spec/heroku/client/pgbackups_spec.rb +43 -0
  55. data/spec/heroku/client/rendezvous_spec.rb +62 -0
  56. data/spec/heroku/client/ssl_endpoint_spec.rb +48 -0
  57. data/spec/heroku/client_spec.rb +564 -0
  58. data/spec/heroku/command/addons_spec.rb +585 -0
  59. data/spec/heroku/command/apps_spec.rb +351 -0
  60. data/spec/heroku/command/auth_spec.rb +38 -0
  61. data/spec/heroku/command/base_spec.rb +109 -0
  62. data/spec/heroku/command/certs_spec.rb +178 -0
  63. data/spec/heroku/command/config_spec.rb +144 -0
  64. data/spec/heroku/command/db_spec.rb +110 -0
  65. data/spec/heroku/command/domains_spec.rb +87 -0
  66. data/spec/heroku/command/drains_spec.rb +34 -0
  67. data/spec/heroku/command/git_spec.rb +116 -0
  68. data/spec/heroku/command/help_spec.rb +93 -0
  69. data/spec/heroku/command/keys_spec.rb +120 -0
  70. data/spec/heroku/command/labs_spec.rb +99 -0
  71. data/spec/heroku/command/logs_spec.rb +60 -0
  72. data/spec/heroku/command/maintenance_spec.rb +51 -0
  73. data/spec/heroku/command/pg_spec.rb +223 -0
  74. data/spec/heroku/command/pgbackups_spec.rb +280 -0
  75. data/spec/heroku/command/plugins_spec.rb +104 -0
  76. data/spec/heroku/command/ps_spec.rb +195 -0
  77. data/spec/heroku/command/releases_spec.rb +130 -0
  78. data/spec/heroku/command/run_spec.rb +86 -0
  79. data/spec/heroku/command/sharing_spec.rb +59 -0
  80. data/spec/heroku/command/ssl_spec.rb +32 -0
  81. data/spec/heroku/command/stack_spec.rb +46 -0
  82. data/spec/heroku/command/status_spec.rb +48 -0
  83. data/spec/heroku/command/version_spec.rb +16 -0
  84. data/spec/heroku/command_spec.rb +211 -0
  85. data/spec/heroku/helpers/heroku_postgresql_spec.rb +109 -0
  86. data/spec/heroku/helpers_spec.rb +48 -0
  87. data/spec/heroku/plugin_spec.rb +172 -0
  88. data/spec/heroku/updater_spec.rb +44 -0
  89. data/spec/spec.opts +1 -0
  90. data/spec/spec_helper.rb +209 -0
  91. data/spec/support/display_message_matcher.rb +49 -0
  92. data/spec/support/openssl_mock_helper.rb +8 -0
  93. metadata +220 -0
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+ require "heroku/command/logs"
3
+
4
+ describe Heroku::Command::Logs do
5
+ describe "logs" do
6
+ it "runs with no options" do
7
+ stub_core.read_logs("myapp", [])
8
+ execute "logs"
9
+ end
10
+
11
+ it "runs with options" do
12
+ stub_core.read_logs("myapp", [
13
+ "tail=1",
14
+ "num=2",
15
+ "ps=ps.3",
16
+ "source=source.4"
17
+ ])
18
+ execute "logs --tail --num 2 --ps ps.3 --source source.4"
19
+ end
20
+
21
+ describe "with log output" do
22
+ before(:each) do
23
+ stub_core.read_logs("myapp", []).yields("2011-01-01T00:00:00+00:00 app[web.1]: test")
24
+ end
25
+
26
+ it "prettifies tty output" do
27
+ old_stdout_isatty = $stdout.isatty
28
+ stub($stdout).isatty.returns(true)
29
+ stderr, stdout = execute("logs")
30
+ stderr.should == ""
31
+ stdout.should == <<-STDOUT
32
+ \e[36m2011-01-01T00:00:00+00:00 app[web.1]:\e[0m test
33
+ STDOUT
34
+ stub($stdout).isatty.returns(old_stdout_isatty)
35
+ end
36
+
37
+ it "does not use ansi if stdout is not a tty" do
38
+ old_stdout_isatty = $stdout.isatty
39
+ stub($stdout).isatty.returns(false)
40
+ stderr, stdout = execute("logs")
41
+ stderr.should == ""
42
+ stdout.should == <<-STDOUT
43
+ 2011-01-01T00:00:00+00:00 app[web.1]: test
44
+ STDOUT
45
+ stub($stdout).isatty.returns(old_stdout_isatty)
46
+ end
47
+
48
+ it "does not use ansi if TERM is not set" do
49
+ term = ENV.delete("TERM")
50
+ stderr, stdout = execute("logs")
51
+ stderr.should == ""
52
+ stdout.should == <<-STDOUT
53
+ 2011-01-01T00:00:00+00:00 app[web.1]: test
54
+ STDOUT
55
+ ENV["TERM"] = term
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+ require "heroku/command/maintenance"
3
+
4
+ module Heroku::Command
5
+ describe Maintenance do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ api.post_app("name" => "myapp", "stack" => "cedar")
10
+ end
11
+
12
+ after(:each) do
13
+ api.delete_app("myapp")
14
+ end
15
+
16
+ it "displays off for maintenance mode of an app" do
17
+ stderr, stdout = execute("maintenance")
18
+ stderr.should == ""
19
+ stdout.should == <<-STDOUT
20
+ off
21
+ STDOUT
22
+ end
23
+
24
+ it "displays on for maintenance mode of an app" do
25
+ api.post_app_maintenance('myapp', '1')
26
+
27
+ stderr, stdout = execute("maintenance")
28
+ stderr.should == ""
29
+ stdout.should == <<-STDOUT
30
+ on
31
+ STDOUT
32
+ end
33
+
34
+ it "turns on maintenance mode for the app" do
35
+ stderr, stdout = execute("maintenance:on")
36
+ stderr.should == ""
37
+ stdout.should == <<-STDOUT
38
+ Enabling maintenance mode for myapp... done
39
+ STDOUT
40
+ end
41
+
42
+ it "turns off maintenance mode for the app" do
43
+ stderr, stdout = execute("maintenance:off")
44
+ stderr.should == ""
45
+ stdout.should == <<-STDOUT
46
+ Disabling maintenance mode for myapp... done
47
+ STDOUT
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,223 @@
1
+ require "spec_helper"
2
+ require "heroku/command/pg"
3
+
4
+ module Heroku::Command
5
+ describe Pg do
6
+ before do
7
+ stub_core
8
+
9
+ api.post_app "name" => "myapp"
10
+ api.put_config_vars "myapp", {
11
+ "DATABASE_URL" => "postgres://database_url",
12
+ "SHARED_DATABASE_URL" => "postgres://other_database_url",
13
+ "HEROKU_POSTGRESQL_RONIN_URL" => "postgres://ronin_database_url"
14
+ }
15
+ end
16
+
17
+ after do
18
+ api.delete_app "myapp"
19
+ end
20
+
21
+ it "resets the app's database if user confirms" do
22
+ stub_pg.reset
23
+
24
+ stderr, stdout = execute("pg:reset RONIN --confirm myapp")
25
+ stderr.should == ""
26
+ stdout.should == <<-STDOUT
27
+ Resetting HEROKU_POSTGRESQL_RONIN... done
28
+ STDOUT
29
+ end
30
+
31
+ it "resets shared databases" do
32
+ Heroku::Client.any_instance.should_receive(:database_reset).with('myapp')
33
+
34
+ stderr, stdout = execute("pg:reset SHARED_DATABASE --confirm myapp")
35
+ stderr.should == ''
36
+ stdout.should == <<-STDOUT
37
+ Resetting SHARED_DATABASE... done
38
+ STDOUT
39
+ end
40
+
41
+ it "doesn't reset the app's database if the user doesn't confirm" do
42
+ stub_pg.reset
43
+
44
+ stderr, stdout = execute("pg:reset RONIN")
45
+ stderr.should == <<-STDERR
46
+ ! Confirmation did not match myapp. Aborted.
47
+ STDERR
48
+ stdout.should == "
49
+ ! WARNING: Destructive Action
50
+ ! This command will affect the app: myapp
51
+ ! To proceed, type \"myapp\" or re-run this command with --confirm myapp
52
+
53
+ > "
54
+ end
55
+
56
+ context "index" do
57
+ it "requests the info from the server" do
58
+ stub_pg.get_database.returns(:info => [
59
+ {"name"=>"Plan", "values"=>["Ronin"]},
60
+ {"name"=>"Status", "values"=>["available"]},
61
+ {"name"=>"Data Size", "values"=>["1 MB"]},
62
+ {"name"=>"Tables", "values"=>[1]},
63
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
64
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
65
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
66
+ {"name"=>"Conn Info", "values"=>["[Deprecated] Please use `heroku pg:credentials HEROKU_POSTGRESQL_RONIN` to view connection info"]},
67
+ {"name"=>"Maintenance", "values"=>["not required"]}
68
+ ])
69
+
70
+ stderr, stdout = execute("pg")
71
+ stderr.should == ""
72
+ stdout.should == <<-STDOUT
73
+ === HEROKU_POSTGRESQL_RONIN
74
+ Plan: Ronin
75
+ Status: available
76
+ Data Size: 1 MB
77
+ Tables: 1
78
+ PG Version: 9.1.4
79
+ Fork/Follow: Available
80
+ Created: 2011-12-13 00:00 UTC
81
+ Conn Info: [Deprecated] Please use `heroku pg:credentials HEROKU_POSTGRESQL_RONIN` to view connection info
82
+ Maintenance: not required
83
+
84
+ === SHARED_DATABASE
85
+ Data Size: (empty)
86
+
87
+ STDOUT
88
+ end
89
+ end
90
+
91
+ context "info" do
92
+ it "requests the info from the server" do
93
+ stub_pg.get_database.returns(:info => [
94
+ {"name"=>"Plan", "values"=>["Ronin"]},
95
+ {"name"=>"Status", "values"=>["available"]},
96
+ {"name"=>"Data Size", "values"=>["1 MB"]},
97
+ {"name"=>"Tables", "values"=>[1]},
98
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
99
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
100
+ {"name"=>"Forked From", "values"=>["postgres://username:password@postgreshost.com:5432/database_name"], "resolve_db_name" => "true"},
101
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
102
+ {"name"=>"Conn Info", "values"=>["[Deprecated] Please use `heroku pg:credentials HEROKU_POSTGRESQL_RONIN` to view connection info"]},
103
+ {"name"=>"Maintenance", "values"=>["not required"]}
104
+ ])
105
+
106
+ stderr, stdout = execute("pg:info RONIN")
107
+ stderr.should == ""
108
+ stdout.should == <<-STDOUT
109
+ === HEROKU_POSTGRESQL_RONIN
110
+ Plan: Ronin
111
+ Status: available
112
+ Data Size: 1 MB
113
+ Tables: 1
114
+ PG Version: 9.1.4
115
+ Fork/Follow: Available
116
+ Forked From: Database on postgreshost.com:5432/database_name
117
+ Created: 2011-12-13 00:00 UTC
118
+ Conn Info: [Deprecated] Please use `heroku pg:credentials HEROKU_POSTGRESQL_RONIN` to view connection info
119
+ Maintenance: not required
120
+
121
+ STDOUT
122
+ end
123
+ end
124
+
125
+ context "promotion" do
126
+ it "promotes the specified database" do
127
+ stderr, stdout = execute("pg:promote RONIN --confirm myapp")
128
+ stderr.should == ""
129
+ stdout.should == <<-STDOUT
130
+ Promoting HEROKU_POSTGRESQL_RONIN to DATABASE_URL... done
131
+ STDOUT
132
+ api.get_config_vars("myapp").body["DATABASE_URL"].should == "postgres://ronin_database_url"
133
+ end
134
+
135
+ it "promotes the specified database url case-sensitively" do
136
+ stderr, stdout = execute("pg:promote postgres://john:S3nsit1ve@my.example.com/db_name --confirm=myapp")
137
+ stderr.should == ""
138
+ stdout.should == <<-STDOUT
139
+ Promoting Custom URL to DATABASE_URL... done
140
+ STDOUT
141
+ end
142
+
143
+ it "fails if no database is specified" do
144
+ stderr, stdout = execute("pg:promote")
145
+ stderr.should == <<-STDERR
146
+ ! Usage: heroku pg:promote DATABASE
147
+ ! Must specify DATABASE to promote.
148
+ STDERR
149
+ stdout.should == ""
150
+ end
151
+ end
152
+
153
+ context "credential resets" do
154
+ before do
155
+ api.put_config_vars "myapp", {
156
+ "DATABASE_URL" => "postgres:///to_reset_credentials",
157
+ "HEROKU_POSTGRESQL_RESETME_URL" => "postgres:///to_reset_credentials"
158
+ }
159
+ end
160
+
161
+ it "resets credentials and promotes to DATABASE_URL if it's the main DB" do
162
+ stub_pg.rotate_credentials
163
+ stderr, stdout = execute("pg:credentials resetme --reset")
164
+ stderr.should be_empty
165
+ stdout.should == <<-STDOUT
166
+ Resetting credentials for HEROKU_POSTGRESQL_RESETME (DATABASE_URL)... done
167
+ Promoting HEROKU_POSTGRESQL_RESETME (DATABASE_URL)... done
168
+ STDOUT
169
+ end
170
+
171
+ it "does not update DATABASE_URL if it's not the main db" do
172
+ stub_pg.rotate_credentials
173
+ api.put_config_vars "myapp", {
174
+ "DATABASE_URL" => "postgres://to_reset_credentials",
175
+ "HEROKU_POSTGRESQL_RESETME_URL" => "postgres://something_else"
176
+ }
177
+ stderr, stdout = execute("pg:credentials resetme --reset")
178
+ stderr.should be_empty
179
+ stdout.should_not include("Promoting")
180
+ end
181
+
182
+ end
183
+
184
+ context "unfollow" do
185
+ before do
186
+ api.put_config_vars "myapp", {
187
+ "DATABASE_URL" => "postgres://database_url",
188
+ "SHARED_DATABASE_URL" => "postgres://other_database_url",
189
+ "HEROKU_POSTGRESQL_RONIN_URL" => "postgres://ronin_database_url",
190
+ "HEROKU_POSTGRESQL_OTHER_URL" => "postgres://other_database_url"
191
+ }
192
+ end
193
+
194
+ it "sends request to unfollow" do
195
+ hpg_client = double('Heroku::Client::HerokuPostgresql')
196
+ Heroku::Client::HerokuPostgresql.should_receive(:new).twice.with('postgres://other_database_url').and_return(hpg_client)
197
+ hpg_client.should_receive(:unfollow)
198
+ hpg_client.should_receive(:get_database).and_return(
199
+ :following => 'postgresql://user:pass@roninhost/database',
200
+ :info => [
201
+ {"name"=>"Plan", "values"=>["Ronin"]},
202
+ {"name"=>"Status", "values"=>["available"]},
203
+ {"name"=>"Data Size", "values"=>["1 MB"]},
204
+ {"name"=>"Tables", "values"=>[1]},
205
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
206
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
207
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
208
+ {"name"=>"Conn Info", "values"=>["[Deprecated] Please use `heroku pg:credentials HEROKU_POSTGRESQL_RONIN` to view connection info"]},
209
+ {"name"=>"Maintenance", "values"=>["not required"]}
210
+ ]
211
+ )
212
+ stderr, stdout = execute("pg:unfollow HEROKU_POSTGRESQL_OTHER --confirm myapp")
213
+ stderr.should == ""
214
+ stdout.should == <<-STDOUT
215
+ ! HEROKU_POSTGRESQL_OTHER will become writable and no longer
216
+ ! follow Database on roninhost:5432/database. This cannot be undone.
217
+ Unfollowing HEROKU_POSTGRESQL_OTHER... done
218
+ STDOUT
219
+ end
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,280 @@
1
+ require "spec_helper"
2
+ require "heroku/command/pgbackups"
3
+
4
+ module Heroku::Command
5
+ describe Pgbackups do
6
+ before do
7
+ @pgbackups = prepare_command(Pgbackups)
8
+ @pgbackups.heroku.stub!(:info).and_return({})
9
+
10
+ api.post_app("name" => "myapp")
11
+ api.put_config_vars(
12
+ "myapp",
13
+ {
14
+ "DATABASE_URL" => "postgres://database",
15
+ "HEROKU_POSTGRESQL_IVORY" => "postgres://database",
16
+ "PGBACKUPS_URL" => "https://ip:password@pgbackups.heroku.com/client"
17
+ }
18
+ )
19
+ end
20
+
21
+ after do
22
+ api.delete_app("myapp")
23
+ end
24
+
25
+ it "requests a pgbackups transfer list for the index command" do
26
+ stub_core
27
+ stub_pgbackups.get_transfers.returns([{
28
+ "created_at" => "2012-01-01 12:00:00 +0000",
29
+ "from_name" => "DATABASE",
30
+ "size" => "1024",
31
+ "to_name" => "BACKUP",
32
+ "to_url" => "s3://bucket/userid/b001.dump"
33
+ }])
34
+
35
+ stderr, stdout = execute("pgbackups")
36
+ stderr.should == ""
37
+ stdout.should == <<-STDOUT
38
+ ID Backup Time Size Database
39
+ ---- ------------------------- ---- --------
40
+ b001 2012-01-01 12:00:00 +0000 1024 DATABASE
41
+ STDOUT
42
+ end
43
+
44
+ describe "single backup" do
45
+ it "gets the url for the latest backup if nothing is specified" do
46
+ stub_core
47
+ stub_pgbackups.get_latest_backup.returns({"public_url" => "http://latest/backup.dump"})
48
+
49
+ old_stdout_isatty = STDOUT.isatty
50
+ $stdout.stub!(:isatty).and_return(true)
51
+ stderr, stdout = execute("pgbackups:url")
52
+ stderr.should == ""
53
+ stdout.should == <<-STDOUT
54
+ http://latest/backup.dump
55
+ STDOUT
56
+ $stdout.stub!(:isatty).and_return(old_stdout_isatty)
57
+ end
58
+
59
+ it "gets the url for the named backup if a name is specified" do
60
+ stub_pgbackups.get_backup.with("b001").returns({"public_url" => "http://latest/backup.dump" })
61
+
62
+ old_stdout_isatty = STDOUT.isatty
63
+ $stdout.stub!(:isatty).and_return(true)
64
+ stderr, stdout = execute("pgbackups:url b001")
65
+ stderr.should == ""
66
+ stdout.should == <<-STDOUT
67
+ http://latest/backup.dump
68
+ STDOUT
69
+ $stdout.stub!(:isatty).and_return(old_stdout_isatty)
70
+ end
71
+
72
+ it "should capture a backup when requested" do
73
+ from_name, from_url = "FROM_NAME", "postgres://from/bar"
74
+ backup_obj = {'to_url' => "s3://bucket/userid/b001.dump"}
75
+
76
+ @pgbackups.stub!(:args).and_return([])
77
+ @pgbackups.stub!(:hpg_resolve).and_return([from_name, from_url])
78
+ @pgbackups.stub!(:transfer!).with(from_url, from_name, nil, "BACKUP", {:expire => nil}).and_return(backup_obj)
79
+ @pgbackups.stub!(:poll_transfer!).with(backup_obj).and_return(backup_obj)
80
+
81
+ @pgbackups.capture
82
+ end
83
+
84
+ it "should send expiration flag to client if specified on args" do
85
+ from_name, from_url = "FROM_NAME", "postgres://from/bar"
86
+ backup_obj = {'to_url' => "s3://bucket/userid/b001.dump"}
87
+
88
+ @pgbackups.stub!(:options).and_return({:expire => true})
89
+ @pgbackups.stub!(:hpg_resolve).and_return([from_name, from_url])
90
+ @pgbackups.stub!(:transfer!).with(from_url, from_name, nil, "BACKUP", {:expire => true}).and_return(backup_obj)
91
+ @pgbackups.stub!(:poll_transfer!).with(backup_obj).and_return(backup_obj)
92
+
93
+ @pgbackups.capture
94
+ end
95
+
96
+ it "destroys no backup without a name" do
97
+ stub_core
98
+ stderr, stdout = execute("pgbackups:destroy")
99
+ stderr.should == <<-STDERR
100
+ ! Usage: heroku pgbackups:destroy BACKUP_ID
101
+ ! Must specify BACKUP_ID to destroy.
102
+ STDERR
103
+ stdout.should == ""
104
+ end
105
+
106
+ it "destroys a backup" do
107
+ stub_core
108
+ stub_pgbackups.get_backup("b001").returns({})
109
+ stub_pgbackups.delete_backup("b001").returns({})
110
+
111
+ stderr, stdout = execute("pgbackups:destroy b001")
112
+ stderr.should == ""
113
+ stdout.should == <<-STDOUT
114
+ Destroying b001... done
115
+ STDOUT
116
+ end
117
+
118
+ it "aborts if no database addon is present" do
119
+ api.delete_config_var("myapp", "DATABASE_URL")
120
+ stub_core
121
+ stderr, stdout = execute("pgbackups:capture")
122
+ stderr.should == <<-STDERR
123
+ ! Your app has no databases.
124
+ STDERR
125
+ stdout.should == ""
126
+ end
127
+
128
+ context "on errors" do
129
+ def stub_failed_capture(log)
130
+ @backup_obj = {
131
+ "error_at" => Time.now.to_s,
132
+ "finished_at" => Time.now.to_s,
133
+ "log" => log,
134
+ 'to_url' => 'postgres://from/bar'
135
+ }
136
+ stub_core
137
+ stub_pgbackups.create_transfer.returns(@backup_obj)
138
+ stub_pgbackups.get_transfer.returns(@backup_obj)
139
+ end
140
+
141
+ it 'aborts on a generic error' do
142
+ stub_failed_capture "something generic"
143
+ stderr, stdout = execute("pgbackups:capture")
144
+ stderr.should == <<-STDERR
145
+ ! An error occurred and your backup did not finish.
146
+ STDERR
147
+ stdout.should == <<-STDOUT
148
+
149
+ HEROKU_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
150
+
151
+ \r\e[0K... 0 -
152
+ STDOUT
153
+ end
154
+
155
+ it 'aborts and informs when the database isnt up yet' do
156
+ stub_failed_capture 'could not translate host name "ec2-42-42-42-42.compute-1.amazonaws.com" to address: Name or service not known'
157
+ stderr, stdout = execute("pgbackups:capture")
158
+ stderr.should == <<-STDERR
159
+ ! An error occurred and your backup did not finish.
160
+ ! The database is not yet online. Please try again.
161
+ STDERR
162
+ stdout.should == <<-STDOUT
163
+
164
+ HEROKU_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
165
+
166
+ \r\e[0K... 0 -
167
+ STDOUT
168
+ end
169
+
170
+ it 'aborts and informs when the credentials are incorrect' do
171
+ stub_failed_capture 'psql: FATAL: database "randomname" does not exist'
172
+ stderr, stdout = execute("pgbackups:capture")
173
+ stderr.should == <<-STDERR
174
+ ! An error occurred and your backup did not finish.
175
+ ! The database credentials are incorrect.
176
+ STDERR
177
+ stdout.should == <<-STDOUT
178
+
179
+ HEROKU_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
180
+
181
+ \r\e[0K... 0 -
182
+ STDOUT
183
+ end
184
+ end
185
+ end
186
+
187
+ context "restore" do
188
+ before do
189
+ from_name, from_url = "FROM_NAME", "postgres://fromhost/database"
190
+
191
+ @pgbackups_client = mock("pgbackups_client")
192
+ @pgbackups.stub!(:pgbackup_client).and_return(@pgbackups_client)
193
+ end
194
+
195
+ it "should receive a confirm_command on restore" do
196
+ @pgbackups_client.stub!(:get_latest_backup).and_return({"to_url" => "s3://bucket/user/bXXX.dump"})
197
+
198
+ @pgbackups.should_receive(:confirm_command).and_return(false)
199
+ @pgbackups_client.should_not_receive(:transfer!)
200
+
201
+ @pgbackups.restore
202
+ end
203
+
204
+ it "aborts if no database addon is present" do
205
+ @pgbackups.should_receive(:hpg_resolve).and_raise(SystemExit)
206
+ lambda { @pgbackups.restore }.should raise_error(SystemExit)
207
+ end
208
+
209
+ context "for commands which perform restores" do
210
+ before do
211
+ @backup_obj = {
212
+ "to_name" => "TO_NAME",
213
+ "to_url" => "s3://bucket/userid/bXXX.dump",
214
+ "from_url" => "FROM_NAME",
215
+ "from_name" => "postgres://databasehost/dbname"
216
+ }
217
+
218
+ @pgbackups.stub!(:confirm_command).and_return(true)
219
+ @pgbackups_client.should_receive(:create_transfer).and_return(@backup_obj)
220
+ @pgbackups.stub!(:poll_transfer!).and_return(@backup_obj)
221
+ end
222
+
223
+ it "should default to the latest backup" do
224
+ @pgbackups.stub(:args).and_return([])
225
+ @pgbackups_client.should_receive(:get_latest_backup).and_return(@backup_obj)
226
+ @pgbackups.restore
227
+ end
228
+
229
+ it "should restore the named backup" do
230
+ name = "backupname"
231
+ args = ['DATABASE', name]
232
+ @pgbackups.stub(:args).and_return(args)
233
+ @pgbackups.stub(:shift_argument).and_return(*args)
234
+ @pgbackups.stub(:hpg_resolve).and_return([name])
235
+ @pgbackups_client.should_receive(:get_backup).with(name).and_return(@backup_obj)
236
+ @pgbackups.restore
237
+ end
238
+
239
+ it "should handle external restores" do
240
+ args = ['db_name_gets_shifted_out_in_resolve_db', 'http://external/file.dump']
241
+ @pgbackups.stub(:args).and_return(args)
242
+ @pgbackups.stub(:shift_argument).and_return(*args)
243
+ @pgbackups.stub(:hpg_resolve).and_return(["name", "url"])
244
+ @pgbackups_client.should_not_receive(:get_backup)
245
+ @pgbackups_client.should_not_receive(:get_latest_backup)
246
+ @pgbackups.restore
247
+ end
248
+ end
249
+
250
+ context "on errors" do
251
+ before(:each) do
252
+ @pgbackups_client.stub!(:get_latest_backup).and_return({"to_url" => "s3://bucket/user/bXXX.dump"})
253
+ @pgbackups.stub!(:confirm_command).and_return(true)
254
+ end
255
+
256
+ def stub_error_backup_with_log(log)
257
+ @backup_obj = {
258
+ "error_at" => Time.now.to_s,
259
+ "log" => log
260
+ }
261
+
262
+ @pgbackups_client.should_receive(:create_transfer).and_return(@backup_obj)
263
+ @pgbackups.stub!(:poll_transfer!).and_return(@backup_obj)
264
+ end
265
+
266
+ it 'aborts for a generic error' do
267
+ stub_error_backup_with_log 'something generic'
268
+ @pgbackups.should_receive(:error).with("An error occurred and your restore did not finish.")
269
+ @pgbackups.restore
270
+ end
271
+
272
+ it 'aborts and informs for expired s3 urls' do
273
+ stub_error_backup_with_log 'Invalid dump format: /tmp/aDMyoXPrAX/b031.dump: XML document text'
274
+ @pgbackups.should_receive(:error).with { |message| message.should =~ /backup url is invalid/ }
275
+ @pgbackups.restore
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end