pogo 2.31.2

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