aptible-cli 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +30 -25
- data/lib/aptible/cli/helpers/tunnel.rb +34 -10
- data/lib/aptible/cli/subcommands/db.rb +9 -0
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/helpers/tunnel_spec.rb +38 -1
- data/spec/aptible/cli/subcommands/db_spec.rb +23 -1
- data/spec/mock/ssh +1 -0
- data/spec/mock/ssh_mock.rb +4 -1
- data/spec/shared/mock_ssh_context.rb +6 -0
- metadata +3 -3
- data/spec/mock/ssh +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ca32684c92760fd049ba703618258b837950590
|
4
|
+
data.tar.gz: b251f6a123c54002638a7ef0c5567de892f6cb62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
32
|
-
aptible apps:create HANDLE
|
33
|
-
aptible apps:deprovision
|
34
|
-
aptible apps:scale TYPE NUMBER
|
35
|
-
aptible
|
36
|
-
aptible
|
37
|
-
aptible config
|
38
|
-
aptible config:
|
39
|
-
aptible config:
|
40
|
-
aptible
|
41
|
-
aptible
|
42
|
-
aptible db:
|
43
|
-
aptible db:
|
44
|
-
aptible db:
|
45
|
-
aptible db:
|
46
|
-
aptible db:
|
47
|
-
aptible
|
48
|
-
aptible
|
49
|
-
aptible
|
50
|
-
aptible
|
51
|
-
aptible
|
52
|
-
aptible
|
53
|
-
aptible
|
54
|
-
aptible
|
55
|
-
aptible
|
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 =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
data/lib/aptible/cli/version.rb
CHANGED
@@ -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({ '
|
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!)
|
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
|
data/spec/mock/ssh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ssh_mock.rb
|
data/spec/mock/ssh_mock.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'json'
|
3
3
|
|
4
|
-
raise 'Something went wrong!' if ENV['
|
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
|
+
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-
|
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
|
298
|
+
rubygems_version: 2.6.4
|
299
299
|
signing_key:
|
300
300
|
specification_version: 4
|
301
301
|
summary: Command-line interface for Aptible services
|
data/spec/mock/ssh
DELETED
@@ -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'
|