aptible-cli 0.16.2 → 0.16.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,14 +2,17 @@ require 'spec_helper'
2
2
 
3
3
  describe Aptible::CLI::Agent do
4
4
  let(:token) { 'some-token' }
5
- let(:account) { Fabricate(:account, handle: 'test') }
5
+ let(:account) { Fabricate(:account, handle: 'test', id: 1) }
6
6
  let(:alt_account) { Fabricate(:account, handle: 'alt') }
7
7
  let(:database) { Fabricate(:database, account: account, handle: 'some-db') }
8
8
  let!(:backup) do
9
9
  # created_at: 2016-06-14 13:24:11 +0000
10
10
  Fabricate(
11
11
  :backup,
12
- database: database, created_at: Time.at(1465910651), account: account
12
+ database_with_deleted: database,
13
+ created_at: Time.at(1465910651),
14
+ account: account,
15
+ id: 1
13
16
  )
14
17
  end
15
18
 
@@ -178,4 +181,78 @@ describe Aptible::CLI::Agent do
178
181
  .to raise_error(Thor::Error, 'Could not find database nope')
179
182
  end
180
183
  end
184
+
185
+ describe '#backup:orphaned' do
186
+ before { allow(Aptible::Api::Account).to receive(:all) { [account] } }
187
+ before do
188
+ m = allow(account).to receive(:each_orphaned_backup)
189
+ ages = [
190
+ 1.day, 2.days, 3.days, 4.days,
191
+ 5.days, 2.weeks, 3.weeks, 1.month,
192
+ 1.year
193
+ ]
194
+ ages.each do |age|
195
+ b = Fabricate(:backup, database: database, created_at: age.ago,
196
+ account: account)
197
+ allow(b).to receive(:database_with_deleted).and_return(database)
198
+ m.and_yield(b)
199
+ b
200
+ end
201
+ end
202
+ before { subject.options = { max_age: '1w' } }
203
+
204
+ it 'can show a subset of backups' do
205
+ subject.send('backup:orphaned')
206
+ puts captured_output_text
207
+ expect(captured_output_text.split("\n").size).to eq(5)
208
+ end
209
+
210
+ it 'allows scoping via environment' do
211
+ subject.options = { max_age: '1w', environment: database.account.handle }
212
+ subject.send('backup:orphaned')
213
+ expect(captured_output_text.split("\n").size).to eq(5)
214
+ end
215
+
216
+ it 'shows more backups if requested' do
217
+ subject.options = { max_age: '2y' }
218
+ subject.send('backup:orphaned')
219
+ expect(captured_output_text.split("\n").size).to eq(9)
220
+ end
221
+
222
+ it 'errors out if max_age is invalid' do
223
+ subject.options = { max_age: 'foobar' }
224
+ expect { subject.send('backup:orphaned') }
225
+ .to raise_error(Thor::Error, 'Invalid age: foobar')
226
+ end
227
+ end
228
+
229
+ describe '#backup:purge' do
230
+ it 'fails if the backup cannot be found' do
231
+ expect(Aptible::Api::Backup).to receive(:find)
232
+ .with(1, token: token).and_return(nil)
233
+
234
+ expect { subject.send('backup:purge', 1) }
235
+ .to raise_error('Backup #1 not found')
236
+ end
237
+
238
+ context 'successful purge' do
239
+ let(:op) { Fabricate(:operation, resource: backup) }
240
+
241
+ before do
242
+ expect(Aptible::Api::Backup).to receive(:find)
243
+ .with(1, token: token).and_return(backup)
244
+ end
245
+
246
+ it 'creates a purge operation on the backup' do
247
+ expect(backup).to receive(:create_operation!) do |options|
248
+ expect(options[:type]).to eq('purge')
249
+ op
250
+ end
251
+
252
+ expect(subject).to receive(:attach_to_operation_logs).with(op)
253
+
254
+ subject.send('backup:purge', 1)
255
+ end
256
+ end
257
+ end
181
258
  end
@@ -55,7 +55,7 @@ describe Aptible::CLI::Agent do
55
55
  subject.send('db:create', 'foo')
56
56
  end
57
57
 
58
- it 'creates a new DB with a disk size' do
58
+ it 'creates a new DB with a (implicitly) disk size' do
59
59
  expect_provision_database(
60
60
  { handle: 'foo', type: 'postgresql', initial_disk_size: 200 },
61
61
  { disk_size: 200 }
@@ -65,6 +65,16 @@ describe Aptible::CLI::Agent do
65
65
  subject.send('db:create', 'foo')
66
66
  end
67
67
 
68
+ it 'creates a new DB with a disk-size' do
69
+ expect_provision_database(
70
+ { handle: 'foo', type: 'postgresql', initial_disk_size: 200 },
71
+ { disk_size: 200 }
72
+ )
73
+
74
+ subject.options = { type: 'postgresql', disk_size: 200 }
75
+ subject.send('db:create', 'foo')
76
+ end
77
+
68
78
  it 'deprovisions the database if the operation cannot be created' do
69
79
  db = Fabricate(:database)
70
80
 
@@ -377,7 +387,7 @@ describe Aptible::CLI::Agent do
377
387
  expect(captured_logs).to match(/restarting foobar/i)
378
388
  end
379
389
 
380
- it 'allows restarting a database with a disk size' do
390
+ it 'allows restarting a database with (implicitly disk) size' do
381
391
  expect(database).to receive(:create_operation!)
382
392
  .with(type: 'restart', disk_size: 40).and_return(op)
383
393
 
@@ -389,6 +399,18 @@ describe Aptible::CLI::Agent do
389
399
  expect(captured_logs).to match(/restarting foobar/i)
390
400
  end
391
401
 
402
+ it 'allows restarting a database with a disk-size' do
403
+ expect(database).to receive(:create_operation!)
404
+ .with(type: 'restart', disk_size: 40).and_return(op)
405
+
406
+ expect(subject).to receive(:attach_to_operation_logs).with(op)
407
+
408
+ subject.options = { disk_size: 40 }
409
+ subject.send('db:restart', handle)
410
+
411
+ expect(captured_logs).to match(/restarting foobar/i)
412
+ end
413
+
392
414
  it 'fails if the DB is not found' do
393
415
  expect { subject.send('db:restart', 'nope') }
394
416
  .to raise_error(Thor::Error, 'Could not find database nope')
@@ -439,6 +461,129 @@ describe Aptible::CLI::Agent do
439
461
  end
440
462
  end
441
463
 
464
+ describe '#db:replicate' do
465
+ let(:databases) { [] }
466
+ before { allow(Aptible::Api::Database).to receive(:all) { databases } }
467
+
468
+ def expect_replicate_database(opts = {})
469
+ master = Fabricate(:database, handle: 'master')
470
+ databases << master
471
+ replica = Fabricate(:database,
472
+ account: master.account,
473
+ handle: 'replica')
474
+
475
+ op = Fabricate(:operation)
476
+
477
+ params = { type: 'replicate', handle: 'replica' }.merge(opts)
478
+ params[:disk_size] = params.delete(:size) if params[:size]
479
+ expect(master).to receive(:create_operation!)
480
+ .with(**params).and_return(op)
481
+
482
+ expect(subject).to receive(:attach_to_operation_logs).with(op) do
483
+ databases << replica
484
+ replica
485
+ end
486
+
487
+ provision = Fabricate(:operation)
488
+
489
+ expect(replica).to receive_message_chain(:operations, :last)
490
+ .and_return(provision)
491
+
492
+ expect(subject).to receive(:attach_to_operation_logs).with(provision)
493
+
494
+ expect(replica).to receive(:reload).and_return(replica)
495
+
496
+ subject.options = opts
497
+ subject.send('db:replicate', 'master', 'replica')
498
+
499
+ expect(captured_logs).to match(/replicating master/i)
500
+ end
501
+
502
+ it 'allows replicating an existing database' do
503
+ expect_replicate_database
504
+ end
505
+
506
+ it 'allows replicating a database with a container size' do
507
+ expect_replicate_database(container_size: 40)
508
+ end
509
+
510
+ it 'allows replicating a database with an (implicitly) disk size option' do
511
+ expect_replicate_database(size: 40)
512
+ end
513
+
514
+ it 'allows replicating a database with a disk-size option' do
515
+ expect_replicate_database(disk_size: 40)
516
+ end
517
+
518
+ it 'fails if the DB is not found' do
519
+ expect { subject.send('db:replicate', 'nope', 'replica') }
520
+ .to raise_error(Thor::Error, 'Could not find database nope')
521
+ end
522
+
523
+ it 'allows logical replication of a database with --version set' do
524
+ master = Fabricate(:database, handle: 'master')
525
+ databases << master
526
+ replica = Fabricate(:database,
527
+ account: master.account,
528
+ handle: 'replica')
529
+
530
+ dbimg = Fabricate(:database_image,
531
+ type: 'postgresql',
532
+ version: 10,
533
+ docker_repo: 'aptible/postgresql:10')
534
+
535
+ expect(subject).to receive(:find_database_image).with('postgresql', 10)
536
+ .and_return(dbimg)
537
+
538
+ op = Fabricate(:operation)
539
+
540
+ params = { type: 'replicate_logical', handle: 'replica',
541
+ docker_ref: dbimg.docker_repo }
542
+ expect(master).to receive(:create_operation!)
543
+ .with(**params).and_return(op)
544
+
545
+ expect(subject).to receive(:attach_to_operation_logs).with(op) do
546
+ databases << replica
547
+ replica
548
+ end
549
+
550
+ provision = Fabricate(:operation)
551
+
552
+ expect(replica).to receive_message_chain(:operations, :last)
553
+ .and_return(provision)
554
+
555
+ expect(subject).to receive(:attach_to_operation_logs).with(provision)
556
+
557
+ expect(replica).to receive(:reload).and_return(replica)
558
+
559
+ subject.options = { logical: true, version: 10 }
560
+ subject.send('db:replicate', 'master', 'replica')
561
+
562
+ expect(captured_logs).to match(/replicating master/i)
563
+ end
564
+
565
+ it 'fails if logical replication requested without --version' do
566
+ master = Fabricate(:database, handle: 'master', type: 'postgresql')
567
+ databases << master
568
+
569
+ subject.options = { type: 'replicate', handle: 'replica', logical: true }
570
+ expect { subject.send('db:replicate', 'master', 'replica') }
571
+ .to raise_error(Thor::Error, '--version is required for logical ' \
572
+ 'replication')
573
+ end
574
+
575
+ it 'fails if logical replication requested for non-postgres db' do
576
+ master = Fabricate(:database, handle: 'master', type: 'mysql')
577
+ databases << master
578
+
579
+ subject.options = { type: 'replicate', handle: 'replica',
580
+ logical: true, version: 10 }
581
+ expect { subject.send('db:replicate', 'master', 'replica') }
582
+ .to raise_error(Thor::Error, 'Logical replication only works for ' \
583
+ 'PostgreSQL')
584
+ end
585
+ end
586
+
442
587
  describe '#db:dump' do
443
588
  it 'should fail if database is non-existent' do
444
589
  allow(Aptible::Api::Database).to receive(:all) { [] }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aptible-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.2
4
+ version: 0.16.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-11 00:00:00.000000000 Z
11
+ date: 2020-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-resource
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.0'
33
+ version: '1.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.0'
40
+ version: '1.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: aptible-auth
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.0'
47
+ version: 1.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.0'
54
+ version: 1.1.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: aptible-billing
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -277,7 +277,9 @@ files:
277
277
  - lib/aptible/cli/helpers/database.rb
278
278
  - lib/aptible/cli/helpers/environment.rb
279
279
  - lib/aptible/cli/helpers/operation.rb
280
+ - lib/aptible/cli/helpers/security_key.rb
280
281
  - lib/aptible/cli/helpers/ssh.rb
282
+ - lib/aptible/cli/helpers/system.rb
281
283
  - lib/aptible/cli/helpers/token.rb
282
284
  - lib/aptible/cli/helpers/tunnel.rb
283
285
  - lib/aptible/cli/helpers/vhost.rb
@@ -372,8 +374,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
372
374
  - !ruby/object:Gem::Version
373
375
  version: '0'
374
376
  requirements: []
375
- rubyforge_project:
376
- rubygems_version: 2.7.6
377
+ rubygems_version: 3.0.3
377
378
  signing_key:
378
379
  specification_version: 4
379
380
  summary: Command-line interface for Aptible services