azuki 0.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.
- checksums.yaml +7 -0
- data/README.md +71 -0
- data/bin/azuki +17 -0
- data/data/cacert.pem +3988 -0
- data/lib/azuki.rb +17 -0
- data/lib/azuki/auth.rb +339 -0
- data/lib/azuki/cli.rb +38 -0
- data/lib/azuki/client.rb +764 -0
- data/lib/azuki/client/azuki_postgresql.rb +141 -0
- data/lib/azuki/client/cisaurus.rb +26 -0
- data/lib/azuki/client/pgbackups.rb +113 -0
- data/lib/azuki/client/rendezvous.rb +108 -0
- data/lib/azuki/client/ssl_endpoint.rb +25 -0
- data/lib/azuki/command.rb +294 -0
- data/lib/azuki/command/account.rb +23 -0
- data/lib/azuki/command/accounts.rb +34 -0
- data/lib/azuki/command/addons.rb +305 -0
- data/lib/azuki/command/apps.rb +393 -0
- data/lib/azuki/command/auth.rb +86 -0
- data/lib/azuki/command/base.rb +230 -0
- data/lib/azuki/command/certs.rb +209 -0
- data/lib/azuki/command/config.rb +137 -0
- data/lib/azuki/command/db.rb +218 -0
- data/lib/azuki/command/domains.rb +85 -0
- data/lib/azuki/command/drains.rb +46 -0
- data/lib/azuki/command/fork.rb +164 -0
- data/lib/azuki/command/git.rb +64 -0
- data/lib/azuki/command/help.rb +179 -0
- data/lib/azuki/command/keys.rb +115 -0
- data/lib/azuki/command/labs.rb +147 -0
- data/lib/azuki/command/logs.rb +45 -0
- data/lib/azuki/command/maintenance.rb +61 -0
- data/lib/azuki/command/pg.rb +269 -0
- data/lib/azuki/command/pgbackups.rb +329 -0
- data/lib/azuki/command/plugins.rb +110 -0
- data/lib/azuki/command/ps.rb +232 -0
- data/lib/azuki/command/regions.rb +22 -0
- data/lib/azuki/command/releases.rb +124 -0
- data/lib/azuki/command/run.rb +180 -0
- data/lib/azuki/command/sharing.rb +89 -0
- data/lib/azuki/command/ssl.rb +43 -0
- data/lib/azuki/command/stack.rb +62 -0
- data/lib/azuki/command/status.rb +51 -0
- data/lib/azuki/command/update.rb +47 -0
- data/lib/azuki/command/version.rb +23 -0
- data/lib/azuki/deprecated.rb +5 -0
- data/lib/azuki/deprecated/help.rb +38 -0
- data/lib/azuki/distribution.rb +9 -0
- data/lib/azuki/excon.rb +9 -0
- data/lib/azuki/helpers.rb +517 -0
- data/lib/azuki/helpers/azuki_postgresql.rb +165 -0
- data/lib/azuki/helpers/log_displayer.rb +70 -0
- data/lib/azuki/plugin.rb +163 -0
- data/lib/azuki/updater.rb +171 -0
- data/lib/azuki/version.rb +3 -0
- data/lib/vendor/azuki/okjson.rb +598 -0
- data/spec/azuki/auth_spec.rb +256 -0
- data/spec/azuki/client/azuki_postgresql_spec.rb +71 -0
- data/spec/azuki/client/pgbackups_spec.rb +43 -0
- data/spec/azuki/client/rendezvous_spec.rb +62 -0
- data/spec/azuki/client/ssl_endpoint_spec.rb +48 -0
- data/spec/azuki/client_spec.rb +564 -0
- data/spec/azuki/command/addons_spec.rb +601 -0
- data/spec/azuki/command/apps_spec.rb +351 -0
- data/spec/azuki/command/auth_spec.rb +38 -0
- data/spec/azuki/command/base_spec.rb +109 -0
- data/spec/azuki/command/certs_spec.rb +178 -0
- data/spec/azuki/command/config_spec.rb +144 -0
- data/spec/azuki/command/db_spec.rb +110 -0
- data/spec/azuki/command/domains_spec.rb +87 -0
- data/spec/azuki/command/drains_spec.rb +34 -0
- data/spec/azuki/command/fork_spec.rb +56 -0
- data/spec/azuki/command/git_spec.rb +144 -0
- data/spec/azuki/command/help_spec.rb +93 -0
- data/spec/azuki/command/keys_spec.rb +120 -0
- data/spec/azuki/command/labs_spec.rb +100 -0
- data/spec/azuki/command/logs_spec.rb +60 -0
- data/spec/azuki/command/maintenance_spec.rb +51 -0
- data/spec/azuki/command/pg_spec.rb +236 -0
- data/spec/azuki/command/pgbackups_spec.rb +307 -0
- data/spec/azuki/command/plugins_spec.rb +104 -0
- data/spec/azuki/command/ps_spec.rb +195 -0
- data/spec/azuki/command/releases_spec.rb +130 -0
- data/spec/azuki/command/run_spec.rb +83 -0
- data/spec/azuki/command/sharing_spec.rb +59 -0
- data/spec/azuki/command/stack_spec.rb +46 -0
- data/spec/azuki/command/status_spec.rb +48 -0
- data/spec/azuki/command/version_spec.rb +16 -0
- data/spec/azuki/command_spec.rb +211 -0
- data/spec/azuki/helpers/azuki_postgresql_spec.rb +155 -0
- data/spec/azuki/helpers_spec.rb +48 -0
- data/spec/azuki/plugin_spec.rb +172 -0
- data/spec/azuki/updater_spec.rb +44 -0
- data/spec/helper/legacy_help.rb +16 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +224 -0
- data/spec/support/display_message_matcher.rb +49 -0
- data/spec/support/openssl_mock_helper.rb +8 -0
- metadata +211 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/pgbackups"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
describe Pgbackups do
|
|
6
|
+
before do
|
|
7
|
+
@pgbackups = prepare_command(Pgbackups)
|
|
8
|
+
@pgbackups.azuki.stub!(:info).and_return({})
|
|
9
|
+
|
|
10
|
+
api.post_app("name" => "example")
|
|
11
|
+
api.put_config_vars(
|
|
12
|
+
"example",
|
|
13
|
+
{
|
|
14
|
+
"DATABASE_URL" => "postgres://database",
|
|
15
|
+
"AZUKI_POSTGRESQL_IVORY" => "postgres://database",
|
|
16
|
+
"PGBACKUPS_URL" => "https://ip:password@pgbackups.azukiapp.com/client"
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
@pgbackups.stub :app_attachments => mock_attachments
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:mock_attachments) {
|
|
23
|
+
[
|
|
24
|
+
Azuki::Helpers::AzukiPostgresql::Attachment.new({
|
|
25
|
+
'config_var' => 'AZUKI_POSTGRESQL_IVORY',
|
|
26
|
+
'resource' => {'name' => 'softly-mocking-123',
|
|
27
|
+
'value' => 'postgres://database',
|
|
28
|
+
'type' => 'azuki-postgresql:baku' }})
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
after do
|
|
33
|
+
api.delete_app("example")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "requests a pgbackups transfer list for the index command" do
|
|
37
|
+
stub_core
|
|
38
|
+
stub_pgbackups.get_transfers.returns([{
|
|
39
|
+
"created_at" => "2012-01-01 12:00:00 +0000",
|
|
40
|
+
"started_at" => "2012-01-01 12:00:01 +0000",
|
|
41
|
+
"from_name" => "DATABASE",
|
|
42
|
+
"size" => "1024",
|
|
43
|
+
"progress" => "dump 2048",
|
|
44
|
+
"to_name" => "BACKUP",
|
|
45
|
+
"to_url" => "s3://bucket/userid/b001.dump"
|
|
46
|
+
}])
|
|
47
|
+
|
|
48
|
+
stderr, stdout = execute("pgbackups")
|
|
49
|
+
stderr.should == ""
|
|
50
|
+
stdout.should == <<-STDOUT
|
|
51
|
+
ID Backup Time Status Size Database
|
|
52
|
+
---- ------------------------- --------- ---- --------
|
|
53
|
+
b001 2012-01-01 12:00:00 +0000 Capturing 1024 DATABASE
|
|
54
|
+
STDOUT
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "single backup" do
|
|
58
|
+
let(:from_name) { "FROM_NAME" }
|
|
59
|
+
let(:from_url) { "postgres://from/bar" }
|
|
60
|
+
let(:attachment) { double('attachment', :display_name => from_name, :url => from_url ) }
|
|
61
|
+
before do
|
|
62
|
+
@pgbackups.stub!(:hpg_resolve).and_return(attachment)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "gets the url for the latest backup if nothing is specified" do
|
|
66
|
+
stub_core
|
|
67
|
+
stub_pgbackups.get_latest_backup.returns({"public_url" => "http://latest/backup.dump"})
|
|
68
|
+
|
|
69
|
+
old_stdout_isatty = STDOUT.isatty
|
|
70
|
+
$stdout.stub!(:isatty).and_return(true)
|
|
71
|
+
stderr, stdout = execute("pgbackups:url")
|
|
72
|
+
stderr.should == ""
|
|
73
|
+
stdout.should == <<-STDOUT
|
|
74
|
+
http://latest/backup.dump
|
|
75
|
+
STDOUT
|
|
76
|
+
$stdout.stub!(:isatty).and_return(old_stdout_isatty)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "gets the url for the named backup if a name is specified" do
|
|
80
|
+
stub_pgbackups.get_backup.with("b001").returns({"public_url" => "http://latest/backup.dump" })
|
|
81
|
+
|
|
82
|
+
old_stdout_isatty = STDOUT.isatty
|
|
83
|
+
$stdout.stub!(:isatty).and_return(true)
|
|
84
|
+
stderr, stdout = execute("pgbackups:url b001")
|
|
85
|
+
stderr.should == ""
|
|
86
|
+
stdout.should == <<-STDOUT
|
|
87
|
+
http://latest/backup.dump
|
|
88
|
+
STDOUT
|
|
89
|
+
$stdout.stub!(:isatty).and_return(old_stdout_isatty)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should capture a backup when requested" do
|
|
93
|
+
backup_obj = {'to_url' => "s3://bucket/userid/b001.dump"}
|
|
94
|
+
|
|
95
|
+
@pgbackups.stub!(:args).and_return([])
|
|
96
|
+
@pgbackups.stub!(:transfer!).with(from_url, from_name, nil, "BACKUP", {:expire => nil}).and_return(backup_obj)
|
|
97
|
+
@pgbackups.stub!(:poll_transfer!).with(backup_obj).and_return(backup_obj)
|
|
98
|
+
|
|
99
|
+
@pgbackups.capture
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should send expiration flag to client if specified on args" do
|
|
103
|
+
backup_obj = {'to_url' => "s3://bucket/userid/b001.dump"}
|
|
104
|
+
|
|
105
|
+
@pgbackups.stub!(:options).and_return({:expire => true})
|
|
106
|
+
@pgbackups.stub!(:transfer!).with(from_url, from_name, nil, "BACKUP", {:expire => true}).and_return(backup_obj)
|
|
107
|
+
@pgbackups.stub!(:poll_transfer!).with(backup_obj).and_return(backup_obj)
|
|
108
|
+
|
|
109
|
+
@pgbackups.capture
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "destroys no backup without a name" do
|
|
113
|
+
stub_core
|
|
114
|
+
stderr, stdout = execute("pgbackups:destroy")
|
|
115
|
+
stderr.should == <<-STDERR
|
|
116
|
+
! Usage: azuki pgbackups:destroy BACKUP_ID
|
|
117
|
+
! Must specify BACKUP_ID to destroy.
|
|
118
|
+
STDERR
|
|
119
|
+
stdout.should == ""
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "destroys a backup" do
|
|
123
|
+
stub_core
|
|
124
|
+
stub_pgbackups.get_backup("b001").returns({})
|
|
125
|
+
stub_pgbackups.delete_backup("b001").returns({})
|
|
126
|
+
|
|
127
|
+
stderr, stdout = execute("pgbackups:destroy b001")
|
|
128
|
+
stderr.should == ""
|
|
129
|
+
stdout.should == <<-STDOUT
|
|
130
|
+
Destroying b001... done
|
|
131
|
+
STDOUT
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "aborts if no database addon is present" do
|
|
135
|
+
api.delete_config_var("example", "DATABASE_URL")
|
|
136
|
+
stub_core
|
|
137
|
+
stderr, stdout = execute("pgbackups:capture")
|
|
138
|
+
stderr.should == <<-STDERR
|
|
139
|
+
! Your app has no databases.
|
|
140
|
+
STDERR
|
|
141
|
+
stdout.should == ""
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context "on errors" do
|
|
145
|
+
def stub_failed_capture(log)
|
|
146
|
+
@backup_obj = {
|
|
147
|
+
"error_at" => Time.now.to_s,
|
|
148
|
+
"finished_at" => Time.now.to_s,
|
|
149
|
+
"log" => log,
|
|
150
|
+
'to_url' => 'postgres://from/bar'
|
|
151
|
+
}
|
|
152
|
+
stub_core
|
|
153
|
+
stub_pgbackups.create_transfer.returns(@backup_obj)
|
|
154
|
+
stub_pgbackups.get_transfer.returns(@backup_obj)
|
|
155
|
+
|
|
156
|
+
any_instance_of(Azuki::Command::Pgbackups) do |pgbackups|
|
|
157
|
+
stub(pgbackups).app_attachments.returns(
|
|
158
|
+
mock_attachments
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'aborts on a generic error' do
|
|
164
|
+
stub_failed_capture "something generic"
|
|
165
|
+
stderr, stdout = execute("pgbackups:capture")
|
|
166
|
+
stderr.should == <<-STDERR
|
|
167
|
+
! An error occurred and your backup did not finish.
|
|
168
|
+
! Please run `azuki logs --ps pgbackups` for details.
|
|
169
|
+
STDERR
|
|
170
|
+
stdout.should == <<-STDOUT
|
|
171
|
+
|
|
172
|
+
AZUKI_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
|
|
173
|
+
|
|
174
|
+
\r\e[0K... 0 -
|
|
175
|
+
STDOUT
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'aborts and informs when the database isnt up yet' do
|
|
179
|
+
stub_failed_capture 'could not translate host name "ec2-42-42-42-42.compute-1.amazonaws.com" to address: Name or service not known'
|
|
180
|
+
stderr, stdout = execute("pgbackups:capture")
|
|
181
|
+
stderr.should == <<-STDERR
|
|
182
|
+
! An error occurred and your backup did not finish.
|
|
183
|
+
! Please run `azuki logs --ps pgbackups` for details.
|
|
184
|
+
! The database is not yet online. Please try again.
|
|
185
|
+
STDERR
|
|
186
|
+
stdout.should == <<-STDOUT
|
|
187
|
+
|
|
188
|
+
AZUKI_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
|
|
189
|
+
|
|
190
|
+
\r\e[0K... 0 -
|
|
191
|
+
STDOUT
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'aborts and informs when the credentials are incorrect' do
|
|
195
|
+
stub_failed_capture 'psql: FATAL: database "randomname" does not exist'
|
|
196
|
+
stderr, stdout = execute("pgbackups:capture")
|
|
197
|
+
stderr.should == <<-STDERR
|
|
198
|
+
! An error occurred and your backup did not finish.
|
|
199
|
+
! Please run `azuki logs --ps pgbackups` for details.
|
|
200
|
+
! The database credentials are incorrect.
|
|
201
|
+
STDERR
|
|
202
|
+
stdout.should == <<-STDOUT
|
|
203
|
+
|
|
204
|
+
AZUKI_POSTGRESQL_IVORY (DATABASE_URL) ----backup---> bar
|
|
205
|
+
|
|
206
|
+
\r\e[0K... 0 -
|
|
207
|
+
STDOUT
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
context "restore" do
|
|
213
|
+
let(:attachment) { double('attachment', :display_name => 'someconfigvar', :url => 'postgres://fromhost/database') }
|
|
214
|
+
before do
|
|
215
|
+
from_name, from_url = "FROM_NAME", "postgres://fromhost/database"
|
|
216
|
+
|
|
217
|
+
@pgbackups_client = mock("pgbackups_client")
|
|
218
|
+
@pgbackups.stub!(:pgbackup_client).and_return(@pgbackups_client)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it "should receive a confirm_command on restore" do
|
|
222
|
+
@pgbackups_client.stub!(:get_latest_backup).and_return({"to_url" => "s3://bucket/user/bXXX.dump"})
|
|
223
|
+
|
|
224
|
+
@pgbackups.should_receive(:confirm_command).and_return(false)
|
|
225
|
+
@pgbackups_client.should_not_receive(:transfer!)
|
|
226
|
+
|
|
227
|
+
@pgbackups.restore
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it "aborts if no database addon is present" do
|
|
231
|
+
@pgbackups.should_receive(:hpg_resolve).and_raise(SystemExit)
|
|
232
|
+
lambda { @pgbackups.restore }.should raise_error(SystemExit)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
context "for commands which perform restores" do
|
|
236
|
+
before do
|
|
237
|
+
@backup_obj = {
|
|
238
|
+
"to_name" => "TO_NAME",
|
|
239
|
+
"to_url" => "s3://bucket/userid/bXXX.dump",
|
|
240
|
+
"from_url" => "FROM_NAME",
|
|
241
|
+
"from_name" => "postgres://databasehost/dbname"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@pgbackups.stub!(:confirm_command).and_return(true)
|
|
245
|
+
@pgbackups_client.should_receive(:create_transfer).and_return(@backup_obj)
|
|
246
|
+
@pgbackups.stub!(:poll_transfer!).and_return(@backup_obj)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
it "should default to the latest backup" do
|
|
250
|
+
@pgbackups.stub(:args).and_return([])
|
|
251
|
+
@pgbackups_client.should_receive(:get_latest_backup).and_return(@backup_obj)
|
|
252
|
+
@pgbackups.restore
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
it "should restore the named backup" do
|
|
257
|
+
name = "backupname"
|
|
258
|
+
args = ['DATABASE', name]
|
|
259
|
+
@pgbackups.stub(:args).and_return(args)
|
|
260
|
+
@pgbackups.stub(:shift_argument).and_return(*args)
|
|
261
|
+
@pgbackups.stub(:hpg_resolve).and_return(attachment)
|
|
262
|
+
@pgbackups_client.should_receive(:get_backup).with(name).and_return(@backup_obj)
|
|
263
|
+
@pgbackups.restore
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "should handle external restores" do
|
|
267
|
+
args = ['db_name_gets_shifted_out_in_resolve_db', 'http://external/file.dump']
|
|
268
|
+
@pgbackups.stub(:args).and_return(args)
|
|
269
|
+
@pgbackups.stub(:shift_argument).and_return(*args)
|
|
270
|
+
@pgbackups.stub(:hpg_resolve).and_return(attachment)
|
|
271
|
+
@pgbackups_client.should_not_receive(:get_backup)
|
|
272
|
+
@pgbackups_client.should_not_receive(:get_latest_backup)
|
|
273
|
+
@pgbackups.restore
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
context "on errors" do
|
|
278
|
+
before(:each) do
|
|
279
|
+
@pgbackups_client.stub!(:get_latest_backup).and_return({"to_url" => "s3://bucket/user/bXXX.dump"})
|
|
280
|
+
@pgbackups.stub!(:confirm_command).and_return(true)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def stub_error_backup_with_log(log)
|
|
284
|
+
@backup_obj = {
|
|
285
|
+
"error_at" => Time.now.to_s,
|
|
286
|
+
"log" => log
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@pgbackups_client.should_receive(:create_transfer).and_return(@backup_obj)
|
|
290
|
+
@pgbackups.stub!(:poll_transfer!).and_return(@backup_obj)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it 'aborts for a generic error' do
|
|
294
|
+
stub_error_backup_with_log 'something generic'
|
|
295
|
+
@pgbackups.should_receive(:error).with("An error occurred and your restore did not finish.\nPlease run `azuki logs --ps pgbackups` for details.")
|
|
296
|
+
@pgbackups.restore
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it 'aborts and informs for expired s3 urls' do
|
|
300
|
+
stub_error_backup_with_log 'Invalid dump format: /tmp/aDMyoXPrAX/b031.dump: XML document text'
|
|
301
|
+
@pgbackups.should_receive(:error).with { |message| message.should =~ /backup url is invalid/ }
|
|
302
|
+
@pgbackups.restore
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/plugins"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
include SandboxHelper
|
|
6
|
+
|
|
7
|
+
describe Plugins do
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@plugin = Azuki::Plugin.new("git://github.com/azuki/Plugin.git")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context("install") do
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
Azuki::Plugin.should_receive(:new).with('git://github.com/azuki/Plugin.git').and_return(@plugin)
|
|
17
|
+
@plugin.should_receive(:install).and_return(true)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "installs plugins" do
|
|
21
|
+
Azuki::Plugin.should_receive(:load_plugin).and_return(true)
|
|
22
|
+
stderr, stdout = execute("plugins:install git://github.com/azuki/Plugin.git")
|
|
23
|
+
stderr.should == ""
|
|
24
|
+
stdout.should == <<-STDOUT
|
|
25
|
+
Installing Plugin... done
|
|
26
|
+
STDOUT
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "does not install plugins that do not load" do
|
|
30
|
+
Azuki::Plugin.should_receive(:load_plugin).and_return(false)
|
|
31
|
+
@plugin.should_receive(:uninstall).and_return(true)
|
|
32
|
+
stderr, stdout = execute("plugins:install git://github.com/azuki/Plugin.git")
|
|
33
|
+
stderr.should == '' # normally would have error, but mocks/stubs don't allow
|
|
34
|
+
stdout.should == "Installing Plugin... " # also inaccurate, would end in ' failed'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context("uninstall") do
|
|
40
|
+
|
|
41
|
+
before do
|
|
42
|
+
Azuki::Plugin.should_receive(:new).with('Plugin').and_return(@plugin)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "uninstalls plugins" do
|
|
46
|
+
@plugin.should_receive(:uninstall).and_return(true)
|
|
47
|
+
stderr, stdout = execute("plugins:uninstall Plugin")
|
|
48
|
+
stderr.should == ""
|
|
49
|
+
stdout.should == <<-STDOUT
|
|
50
|
+
Uninstalling Plugin... done
|
|
51
|
+
STDOUT
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "does not uninstall plugins that do not exist" do
|
|
55
|
+
stderr, stdout = execute("plugins:uninstall Plugin")
|
|
56
|
+
stderr.should == <<-STDERR
|
|
57
|
+
! Plugin plugin not found.
|
|
58
|
+
STDERR
|
|
59
|
+
stdout.should == <<-STDOUT
|
|
60
|
+
Uninstalling Plugin... failed
|
|
61
|
+
STDOUT
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context("update") do
|
|
67
|
+
|
|
68
|
+
before do
|
|
69
|
+
Azuki::Plugin.should_receive(:new).with('Plugin').and_return(@plugin)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "updates plugin by name" do
|
|
73
|
+
@plugin.should_receive(:update).and_return(true)
|
|
74
|
+
stderr, stdout = execute("plugins:update Plugin")
|
|
75
|
+
stderr.should == ""
|
|
76
|
+
stdout.should == <<-STDOUT
|
|
77
|
+
Updating Plugin... done
|
|
78
|
+
STDOUT
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "updates all plugins" do
|
|
82
|
+
Azuki::Plugin.should_receive(:list).and_return([], [], ['Plugin'])
|
|
83
|
+
@plugin.should_receive(:update).and_return(true)
|
|
84
|
+
stderr, stdout = execute("plugins:update")
|
|
85
|
+
stderr.should == ""
|
|
86
|
+
stdout.should == <<-STDOUT
|
|
87
|
+
Updating Plugin... done
|
|
88
|
+
STDOUT
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "does not update plugins that do not exist" do
|
|
92
|
+
stderr, stdout = execute("plugins:update Plugin")
|
|
93
|
+
stderr.should == <<-STDERR
|
|
94
|
+
! Plugin plugin not found.
|
|
95
|
+
STDERR
|
|
96
|
+
stdout.should == <<-STDOUT
|
|
97
|
+
Updating Plugin... failed
|
|
98
|
+
STDOUT
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/ps"
|
|
3
|
+
|
|
4
|
+
describe Azuki::Command::Ps do
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
stub_core
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
context("cedar") do
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
api.post_app("name" => "example", "stack" => "cedar")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after(:each) do
|
|
17
|
+
api.delete_app("example")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "ps:dynos errors out on cedar apps" do
|
|
21
|
+
lambda { execute("ps:dynos") }.should raise_error(Azuki::Command::CommandFailed, "For Cedar apps, use `azuki ps`")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "ps:workers errors out on cedar apps" do
|
|
25
|
+
lambda { execute("ps:workers") }.should raise_error(Azuki::Command::CommandFailed, "For Cedar apps, use `azuki ps`")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "ps" do
|
|
29
|
+
|
|
30
|
+
it "displays processes" do
|
|
31
|
+
Azuki::Command::Ps.any_instance.should_receive(:time_ago).exactly(10).times.and_return("2012/09/11 12:34:56 (~ 0s ago)")
|
|
32
|
+
api.post_ps_scale('example', 'web', 10)
|
|
33
|
+
stderr, stdout = execute("ps")
|
|
34
|
+
stderr.should == ""
|
|
35
|
+
stdout.should == <<-STDOUT
|
|
36
|
+
=== web: `bundle exec thin start -p $PORT`
|
|
37
|
+
web.1: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
38
|
+
web.2: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
39
|
+
web.3: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
40
|
+
web.4: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
41
|
+
web.5: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
42
|
+
web.6: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
43
|
+
web.7: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
44
|
+
web.8: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
45
|
+
web.9: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
46
|
+
web.10: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
47
|
+
|
|
48
|
+
STDOUT
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "displays one-off processes" do
|
|
52
|
+
Azuki::Command::Ps.any_instance.should_receive(:time_ago).and_return('2012/09/11 12:34:56 (~ 0s ago)', '2012/09/11 12:34:56 (~ 0s ago)')
|
|
53
|
+
api.post_ps "example", "bash"
|
|
54
|
+
|
|
55
|
+
stderr, stdout = execute("ps")
|
|
56
|
+
stderr.should == ""
|
|
57
|
+
stdout.should == <<-STDOUT
|
|
58
|
+
=== run: one-off processes
|
|
59
|
+
run.1: created 2012/09/11 12:34:56 (~ 0s ago): `bash`
|
|
60
|
+
|
|
61
|
+
=== web: `bundle exec thin start -p $PORT`
|
|
62
|
+
web.1: created 2012/09/11 12:34:56 (~ 0s ago)
|
|
63
|
+
|
|
64
|
+
STDOUT
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe "ps:restart" do
|
|
70
|
+
|
|
71
|
+
it "restarts all processes with no args" do
|
|
72
|
+
stderr, stdout = execute("ps:restart")
|
|
73
|
+
stderr.should == ""
|
|
74
|
+
stdout.should == <<-STDOUT
|
|
75
|
+
Restarting processes... done
|
|
76
|
+
STDOUT
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "restarts one process" do
|
|
80
|
+
stderr, stdout = execute("ps:restart web.1")
|
|
81
|
+
stderr.should == ""
|
|
82
|
+
stdout.should == <<-STDOUT
|
|
83
|
+
Restarting web.1 process... done
|
|
84
|
+
STDOUT
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "restarts a type of process" do
|
|
88
|
+
stderr, stdout = execute("ps:restart web")
|
|
89
|
+
stderr.should == ""
|
|
90
|
+
stdout.should == <<-STDOUT
|
|
91
|
+
Restarting web processes... done
|
|
92
|
+
STDOUT
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "ps:scale" do
|
|
98
|
+
|
|
99
|
+
it "can scale using key/value format" do
|
|
100
|
+
stderr, stdout = execute("ps:scale web=5")
|
|
101
|
+
stderr.should == ""
|
|
102
|
+
stdout.should == <<-STDOUT
|
|
103
|
+
Scaling web processes... done, now running 5
|
|
104
|
+
STDOUT
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "can scale relative amounts" do
|
|
108
|
+
stderr, stdout = execute("ps:scale web+2")
|
|
109
|
+
stderr.should == ""
|
|
110
|
+
stdout.should == <<-STDOUT
|
|
111
|
+
Scaling web processes... done, now running 3
|
|
112
|
+
STDOUT
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "ps:stop" do
|
|
118
|
+
|
|
119
|
+
it "restarts one process" do
|
|
120
|
+
stderr, stdout = execute("ps:restart ps.1")
|
|
121
|
+
stderr.should == ""
|
|
122
|
+
stdout.should == <<-STDOUT
|
|
123
|
+
Restarting ps.1 process... done
|
|
124
|
+
STDOUT
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "restarts a type of process" do
|
|
128
|
+
stderr, stdout = execute("ps:restart ps")
|
|
129
|
+
stderr.should == ""
|
|
130
|
+
stdout.should == <<-STDOUT
|
|
131
|
+
Restarting ps processes... done
|
|
132
|
+
STDOUT
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
context("non-cedar") do
|
|
140
|
+
|
|
141
|
+
before(:each) do
|
|
142
|
+
api.post_app("name" => "example")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
after(:each) do
|
|
146
|
+
api.delete_app("example")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
describe "ps:dynos" do
|
|
150
|
+
|
|
151
|
+
it "displays the current number of dynos" do
|
|
152
|
+
stderr, stdout = execute("ps:dynos")
|
|
153
|
+
stderr.should == ""
|
|
154
|
+
stdout.should == <<-STDOUT
|
|
155
|
+
~ `azuki ps:dynos QTY` has been deprecated and replaced with `azuki ps:scale dynos=QTY`
|
|
156
|
+
example is running 0 dynos
|
|
157
|
+
STDOUT
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "sets the number of dynos" do
|
|
161
|
+
stderr, stdout = execute("ps:dynos 5")
|
|
162
|
+
stderr.should == ""
|
|
163
|
+
stdout.should == <<-STDOUT
|
|
164
|
+
~ `azuki ps:dynos QTY` has been deprecated and replaced with `azuki ps:scale dynos=QTY`
|
|
165
|
+
Scaling dynos... done, now running 5
|
|
166
|
+
STDOUT
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe "ps:workers" do
|
|
172
|
+
|
|
173
|
+
it "displays the current number of workers" do
|
|
174
|
+
stderr, stdout = execute("ps:workers")
|
|
175
|
+
stderr.should == ""
|
|
176
|
+
stdout.should == <<-STDOUT
|
|
177
|
+
~ `azuki ps:workers QTY` has been deprecated and replaced with `azuki ps:scale workers=QTY`
|
|
178
|
+
example is running 0 workers
|
|
179
|
+
STDOUT
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "sets the number of workers" do
|
|
183
|
+
stderr, stdout = execute("ps:workers 5")
|
|
184
|
+
stderr.should == ""
|
|
185
|
+
stdout.should == <<-STDOUT
|
|
186
|
+
~ `azuki ps:workers QTY` has been deprecated and replaced with `azuki ps:scale workers=QTY`
|
|
187
|
+
Scaling workers... done, now running 5
|
|
188
|
+
STDOUT
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
end
|