aptible-cli 0.8.4 → 0.8.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd702a7afbcc8290e375387617c228b388b69918
4
- data.tar.gz: bbbcb95f4ebc9deac6671049c7534c83e2127d1e
3
+ metadata.gz: 7ca32684c92760fd049ba703618258b837950590
4
+ data.tar.gz: b251f6a123c54002638a7ef0c5567de892f6cb62
5
5
  SHA512:
6
- metadata.gz: c975747091a4d3232c190baee45865bd38dcde4fe8c585ac78a3b86050f5e382432ed78e719e6fb3ae947fd5e1f213e765ff336d3e07886d97b26feb4ae6fae9
7
- data.tar.gz: e2b3152c9a2e6a9398b8dc87031b7791b7ad614836d9a548fa9721db3b7864101f53734cf55701940e8c9cd0db93c73b6fbd0ff694c1a7a5f8e0d6733ee02fac
6
+ metadata.gz: 28e54b39f91cb892b9ce5443321b9049290a302b9e38de9cd3b7162b3567499fa5c00abc5d860d31d8661190fdbc692b24ce0186af410b98a4922a0b62cdd70d
7
+ data.tar.gz: 772bb6f34a8a07baee41f1682189dbfa6a31c422802649d74721f028c89cd060ae7cfe4c77f3bfd150c9cb90c04b3ddc52acb6bc52be31f71cf83eafb8b05acf
data/README.md CHANGED
@@ -28,31 +28,36 @@ From `aptible help`:
28
28
 
29
29
  ```
30
30
  Commands:
31
- aptible apps # List all applications
32
- aptible apps:create HANDLE # Create a new application
33
- aptible apps:deprovision # Deprovision an app
34
- aptible apps:scale TYPE NUMBER # Scale app to NUMBER of instances
35
- aptible config # Print an app's current configuration
36
- aptible config:add # Add an ENV variable to an app
37
- aptible config:rm # Remove an ENV variable from an app
38
- aptible config:set # Alias for config:add
39
- aptible config:unset # Alias for config:rm
40
- aptible db:clone SOURCE DEST # Clone a database to create a new one
41
- aptible db:create HANDLE # Create a new database
42
- aptible db:deprovision HANDLE # Deprovision a database
43
- aptible db:dump HANDLE # Dump a remote database to file
44
- aptible db:execute HANDLE SQL_FILE # Executes sql against a database
45
- aptible db:list # List all databases
46
- aptible db:tunnel HANDLE # Create a local tunnel to a database
47
- aptible domains # Print an app's current virtual domains
48
- aptible help [COMMAND] # Describe available commands or one specific command
49
- aptible login # Log in to Aptible
50
- aptible logs # Follows logs from a running app
51
- aptible ps # Display running processes for an app - DEPRECATED
52
- aptible rebuild # Rebuild an app, and restart its services
53
- aptible restart # Restart all services associated with an app
54
- aptible ssh [COMMAND] # Run a command against an app
55
- aptible version # Print Aptible CLI version
31
+ aptible apps # List all applications
32
+ aptible apps:create HANDLE # Create a new application
33
+ aptible apps:deprovision # Deprovision an app
34
+ aptible apps:scale TYPE NUMBER # Scale app to NUMBER of instances
35
+ aptible backup:list DB_HANDLE # List backups for a database
36
+ aptible backup:restore [--handle HANDLE] [--size SIZE_GB] # Restore a backup
37
+ aptible config # Print an app's current configuration
38
+ aptible config:add # Add an ENV variable to an app
39
+ aptible config:rm # Remove an ENV variable from an app
40
+ aptible config:set # Alias for config:add
41
+ aptible config:unset # Alias for config:rm
42
+ aptible db:backup HANDLE # Backup a database
43
+ aptible db:clone SOURCE DEST # Clone a database to create a new one
44
+ aptible db:create HANDLE # Create a new database
45
+ aptible db:deprovision HANDLE # Deprovision a database
46
+ aptible db:dump HANDLE # Dump a remote database to file
47
+ aptible db:execute HANDLE SQL_FILE # Executes sql against a database
48
+ aptible db:list # List all databases
49
+ aptible db:reload HANDLE # Reload a database
50
+ aptible db:tunnel HANDLE # Create a local tunnel to a database
51
+ aptible domains # Print an app's current virtual domains
52
+ aptible help [COMMAND] # Describe available commands or one specific command
53
+ aptible login # Log in to Aptible
54
+ aptible logs # Follows logs from a running app or database
55
+ aptible operation:cancel OPERATION_ID # Cancel a running operation
56
+ aptible ps # Display running processes for an app - DEPRECATED
57
+ aptible rebuild # Rebuild an app, and restart its services
58
+ aptible restart # Restart all services associated with an app
59
+ aptible ssh [COMMAND] # Run a command against an app
60
+ aptible version # Print Aptible CLI version
56
61
  ```
57
62
 
58
63
  ## Contributing
@@ -11,16 +11,18 @@ module Aptible
11
11
  :SIGHUP
12
12
  end
13
13
 
14
+ STOP_TIMEOUT = 5
15
+
14
16
  # The :new_pgroup key specifies the CREATE_NEW_PROCESS_GROUP flag for
15
17
  # CreateProcessW() in the Windows API. This is a Windows only option.
16
18
  # true means the new process is the root process of the new process
17
19
  # group. This flag is necessary to be able to signal the subprocess on
18
20
  # Windows.
19
- SPAWN_OPTS = if Gem.win_platform?
20
- { new_pgroup: true }
21
- else
22
- {}
23
- end
21
+ SPAWN_OPTS = if Gem.win_platform?
22
+ { new_pgroup: true }
23
+ else
24
+ {}
25
+ end
24
26
 
25
27
  class Tunnel
26
28
  def initialize(env, ssh_cmd, socket_path)
@@ -61,18 +63,40 @@ module Aptible
61
63
 
62
64
  def stop
63
65
  raise 'You must call #start before calling #stop' if @pid.nil?
66
+
64
67
  begin
65
68
  Process.kill(STOP_SIGNAL, @pid)
66
69
  rescue Errno::ESRCH
67
- nil # Dear Rubocop: I know what I'm doing.
70
+ # Already dead.
71
+ return
72
+ end
73
+
74
+ begin
75
+ STOP_TIMEOUT.times do
76
+ return if Process.wait(@pid, Process::WNOHANG)
77
+ sleep 1
78
+ end
79
+ Process.kill(:SIGKILL, @pid)
80
+ rescue Errno::ECHILD, Errno::ESRCH
81
+ # Died at some point, that's fine.
68
82
  end
69
- wait
70
83
  end
71
84
 
72
85
  def wait
73
- Process.wait @pid
74
- rescue Errno::ECHILD
75
- nil
86
+ # NOTE: Ruby is kind enough to retry when EINTR is thrown, so we
87
+ # don't need to retry or anything here.
88
+ _, status = Process.wait2(@pid)
89
+
90
+ code = status.exitstatus
91
+
92
+ case code
93
+ when 0
94
+ # No-op: we're happy with this.
95
+ when 124
96
+ raise Thor::Error, 'Tunnel timed out'
97
+ else
98
+ raise Thor::Error, "Tunnel crashed (#{code})"
99
+ end
76
100
  end
77
101
 
78
102
  def port
@@ -129,6 +129,15 @@ module Aptible
129
129
  op = database.create_operation!(type: 'backup')
130
130
  attach_to_operation_logs(op)
131
131
  end
132
+
133
+ desc 'db:reload HANDLE', 'Reload a database'
134
+ option :environment
135
+ define_method 'db:reload' do |handle|
136
+ database = ensure_database(options.merge(db: handle))
137
+ say "Reloading #{database.handle}..."
138
+ op = database.create_operation!(type: 'reload')
139
+ attach_to_operation_logs(op)
140
+ end
132
141
  end
133
142
  end
134
143
  end
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.8.4'.freeze
3
+ VERSION = '0.8.5'.freeze
4
4
  end
5
5
  end
@@ -30,8 +30,21 @@ describe Aptible::CLI::Helpers::Tunnel do
30
30
  expect(mock_argv.shift).to eq('5678:/some.sock')
31
31
  end
32
32
 
33
+ it 'provides the port it picked' do
34
+ helper = described_class.new({}, ['ssh'], '/some.sock')
35
+ helper.start
36
+ port = helper.port
37
+ helper.stop
38
+
39
+ mock_argv = read_mock_argv
40
+ expect(mock_argv.size).to eq(4)
41
+
42
+ expect(mock_argv.shift).to eq('-L')
43
+ expect(mock_argv.shift).to eq("#{port}:/some.sock")
44
+ end
45
+
33
46
  it 'captures and displays tunnel errors' do
34
- helper = described_class.new({ 'FAIL_TUNNEL' => '1' }, ['ssh'],
47
+ helper = described_class.new({ 'SSH_MOCK_FAIL_TUNNEL' => '1' }, ['ssh'],
35
48
  '/some.sock')
36
49
 
37
50
  expect { helper.start(0) }
@@ -47,4 +60,28 @@ describe Aptible::CLI::Helpers::Tunnel do
47
60
  socat = described_class.new({}, [], '/some.sock')
48
61
  expect { socat.stop }.to raise_error(/You must call #start/)
49
62
  end
63
+
64
+ it 'understands an exit status of 0' do
65
+ helper = described_class.new(
66
+ { 'SSH_MOCK_EXITCODE' => '0' }, ['ssh'], '/some.sock'
67
+ )
68
+ helper.start
69
+ helper.wait
70
+ end
71
+
72
+ it 'understands an exit status of 1' do
73
+ helper = described_class.new(
74
+ { 'SSH_MOCK_EXITCODE' => '1' }, ['ssh'], '/some.sock'
75
+ )
76
+ helper.start
77
+ expect { helper.wait }.to raise_error(/tunnel crashed/im)
78
+ end
79
+
80
+ it 'understands an exit status of 124' do
81
+ helper = described_class.new(
82
+ { 'SSH_MOCK_EXITCODE' => '124' }, ['ssh'], '/some.sock'
83
+ )
84
+ helper.start
85
+ expect { helper.wait }.to raise_error(/tunnel timed out/im)
86
+ end
50
87
  end
@@ -199,7 +199,8 @@ describe Aptible::CLI::Agent do
199
199
  let(:op) { Fabricate(:operation) }
200
200
 
201
201
  it 'allows creating a new backup' do
202
- expect(database).to receive(:create_operation!).and_return(op)
202
+ expect(database).to receive(:create_operation!)
203
+ .with(type: 'backup').and_return(op)
203
204
  expect(subject).to receive(:say).with('Backing up foobar...')
204
205
  expect(subject).to receive(:attach_to_operation_logs).with(op)
205
206
 
@@ -211,4 +212,25 @@ describe Aptible::CLI::Agent do
211
212
  .to raise_error(Thor::Error, 'Could not find database nope')
212
213
  end
213
214
  end
215
+
216
+ describe '#db:reload' do
217
+ before { allow(Aptible::Api::Account).to receive(:all) { [account] } }
218
+ before { allow(Aptible::Api::Database).to receive(:all) { [database] } }
219
+
220
+ let(:op) { Fabricate(:operation) }
221
+
222
+ it 'allows reloading a database' do
223
+ expect(database).to receive(:create_operation!)
224
+ .with(type: 'reload').and_return(op)
225
+ expect(subject).to receive(:say).with('Reloading foobar...')
226
+ expect(subject).to receive(:attach_to_operation_logs).with(op)
227
+
228
+ subject.send('db:reload', handle)
229
+ end
230
+
231
+ it 'fails if the DB is not found' do
232
+ expect { subject.send('db:reload', 'nope') }
233
+ .to raise_error(Thor::Error, 'Could not find database nope')
234
+ end
235
+ end
214
236
  end
@@ -0,0 +1 @@
1
+ ssh_mock.rb
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'json'
3
3
 
4
- raise 'Something went wrong!' if ENV['FAIL_TUNNEL']
4
+ raise 'Something went wrong!' if ENV['SSH_MOCK_FAIL_TUNNEL']
5
5
 
6
6
  # Log arguments to SSH_MOCK_OUTFILE
7
7
  File.open(ENV.fetch('SSH_MOCK_OUTFILE'), 'w') do |f|
8
8
  f.write({
9
+ 'pid' => $PID,
9
10
  'argc' => ARGV.size,
10
11
  'argv' => ARGV,
11
12
  'env' => ENV.to_hash
@@ -13,3 +14,5 @@ File.open(ENV.fetch('SSH_MOCK_OUTFILE'), 'w') do |f|
13
14
  end
14
15
 
15
16
  puts 'TUNNEL READY'
17
+
18
+ exit Integer(ENV.fetch('SSH_MOCK_EXITCODE', 0))
@@ -16,6 +16,12 @@ shared_context 'mock ssh' do
16
16
  ClimateControl.modify(env) { example.run }
17
17
  end
18
18
 
19
+ def read_mock_pid
20
+ File.open(ssh_mock_outfile) do |f|
21
+ return JSON.load(f.read).fetch('pid')
22
+ end
23
+ end
24
+
19
25
  def read_mock_argv
20
26
  File.open(ssh_mock_outfile) do |f|
21
27
  return JSON.load(f.read).fetch('argv')
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.8.4
4
+ version: 0.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-25 00:00:00.000000000 Z
11
+ date: 2017-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-api
@@ -295,7 +295,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
295
295
  version: '0'
296
296
  requirements: []
297
297
  rubyforge_project:
298
- rubygems_version: 2.4.5.1
298
+ rubygems_version: 2.6.4
299
299
  signing_key:
300
300
  specification_version: 4
301
301
  summary: Command-line interface for Aptible services
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'json'
3
-
4
- raise 'Something went wrong!' if ENV['FAIL_TUNNEL']
5
-
6
- # Log arguments to SSH_MOCK_OUTFILE
7
- File.open(ENV.fetch('SSH_MOCK_OUTFILE'), 'w') do |f|
8
- f.write({
9
- 'argc' => ARGV.size,
10
- 'argv' => ARGV,
11
- 'env' => ENV.to_hash
12
- }.to_json)
13
- end
14
-
15
- puts 'TUNNEL READY'