aptible-cli 0.6.8 → 0.6.9
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 +4 -4
- data/aptible-cli.gemspec +1 -0
- data/lib/aptible/cli/agent.rb +1 -0
- data/lib/aptible/cli/helpers/database.rb +28 -47
- data/lib/aptible/cli/helpers/tunnel.rb +76 -0
- data/lib/aptible/cli/subcommands/db.rb +30 -15
- data/lib/aptible/cli/subcommands/logs.rb +1 -1
- data/lib/aptible/cli/subcommands/ps.rb +1 -1
- data/lib/aptible/cli/subcommands/ssh.rb +1 -1
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/helpers/tunnel_spec.rb +71 -0
- data/spec/aptible/cli/subcommands/db_spec.rb +15 -4
- data/spec/mock/ssh_mock.rb +18 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c58858b887b9868e017e93d7248f4e7d1f898dc2
|
4
|
+
data.tar.gz: 7b2d52cc1b316205abb7d35c13722a32dccda29a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c326bb7a9cc09e2689804e43b0fa00643eb759dbe838f572b0dc44df32caba6f6b76f1a14375e4aa6c1d5cac83457b8a37274c72f14ed843b6afcaa6acb2287
|
7
|
+
data.tar.gz: 07bd87aa8c414600d845ce133c868ad1a0df02e9c6518f6202717eabd7f12d8d7b95df42cc5a1f4177bb4f315e6950fc4fa6e13b64059ce2f12f8b0b0a6bf2c3
|
data/aptible-cli.gemspec
CHANGED
data/lib/aptible/cli/agent.rb
CHANGED
@@ -44,18 +44,6 @@ module Aptible
|
|
44
44
|
say ''
|
45
45
|
end
|
46
46
|
|
47
|
-
def establish_connection(database, local_port)
|
48
|
-
ENV['ACCESS_TOKEN'] = fetch_token
|
49
|
-
ENV['APTIBLE_DATABASE'] = database.handle
|
50
|
-
|
51
|
-
remote_port = claim_remote_port(database)
|
52
|
-
ENV['TUNNEL_PORT'] = remote_port
|
53
|
-
|
54
|
-
tunnel_args = "-L #{local_port}:localhost:#{remote_port}"
|
55
|
-
command = "ssh #{tunnel_args} #{common_ssh_args(database)}"
|
56
|
-
Kernel.exec(command)
|
57
|
-
end
|
58
|
-
|
59
47
|
def clone_database(source, dest_handle)
|
60
48
|
op = source.create_operation(type: 'clone', handle: dest_handle)
|
61
49
|
poll_for_success(op)
|
@@ -63,35 +51,33 @@ module Aptible
|
|
63
51
|
databases_from_handle(dest_handle, source.account).first
|
64
52
|
end
|
65
53
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
54
|
+
# Creates a local tunnel and yields the helper
|
55
|
+
|
56
|
+
def with_local_tunnel(database, port = 0)
|
57
|
+
env = {
|
58
|
+
'ACCESS_TOKEN' => fetch_token,
|
59
|
+
'APTIBLE_DATABASE' => database.href
|
60
|
+
}
|
61
|
+
command = ['ssh', '-q'] + ssh_args(database)
|
62
|
+
Helpers::Tunnel.new(env, command).tap do |tunnel_helper|
|
63
|
+
tunnel_helper.start(port)
|
64
|
+
yield tunnel_helper
|
65
|
+
tunnel_helper.stop
|
71
66
|
end
|
72
67
|
end
|
73
68
|
|
74
|
-
# Creates a local tunnel and yields the url to it
|
75
|
-
def execute_local_tunnel(database)
|
76
|
-
local_port = random_local_port
|
77
|
-
pid = fork { establish_connection(database, local_port) }
|
78
|
-
|
79
|
-
# TODO: Better test for connection readiness
|
80
|
-
sleep 10
|
69
|
+
# Creates a local PG tunnel and yields the url to it
|
81
70
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
Process.kill('HUP', pid) if pid
|
87
|
-
end
|
71
|
+
def with_postgres_tunnel(database)
|
72
|
+
if database.type != 'postgresql'
|
73
|
+
fail Thor::Error, 'This command only works for PostgreSQL'
|
74
|
+
end
|
88
75
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
port
|
76
|
+
with_local_tunnel(database) do |tunnel_helper|
|
77
|
+
auth = "aptible:#{database.passphrase}"
|
78
|
+
host = "localhost:#{tunnel_helper.port}"
|
79
|
+
yield "postgresql://#{auth}@#{host}/db"
|
80
|
+
end
|
95
81
|
end
|
96
82
|
|
97
83
|
def local_url(database, local_port)
|
@@ -102,20 +88,15 @@ module Aptible
|
|
102
88
|
"127.0.0.1:#{local_port}#{uri.path}"
|
103
89
|
end
|
104
90
|
|
105
|
-
def
|
106
|
-
ENV['ACCESS_TOKEN'] = fetch_token
|
107
|
-
|
108
|
-
`ssh #{common_ssh_args(database)} 2>/dev/null`.chomp
|
109
|
-
end
|
110
|
-
|
111
|
-
def common_ssh_args(database)
|
91
|
+
def ssh_args(database)
|
112
92
|
host = database.account.bastion_host
|
113
93
|
port = database.account.bastion_port
|
114
94
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
95
|
+
['-o', 'SendEnv=APTIBLE_DATABASE',
|
96
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
97
|
+
'-o', 'StrictHostKeyChecking=no',
|
98
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
99
|
+
'-p', port.to_s, "root@#{host}"]
|
119
100
|
end
|
120
101
|
end
|
121
102
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Aptible
|
5
|
+
module CLI
|
6
|
+
module Helpers
|
7
|
+
class Tunnel
|
8
|
+
def initialize(env, cmd)
|
9
|
+
@env = env
|
10
|
+
@cmd = cmd
|
11
|
+
end
|
12
|
+
|
13
|
+
def start(desired_port = 0, err_fd = $stderr)
|
14
|
+
@local_port = desired_port
|
15
|
+
@local_port = random_local_port if @local_port == 0
|
16
|
+
|
17
|
+
# First, grab a remote port
|
18
|
+
out, err, status = Open3.capture3(@env, *@cmd)
|
19
|
+
fail "Failed to request remote port: #{err}" unless status.success?
|
20
|
+
remote_port = out.chomp
|
21
|
+
|
22
|
+
# Then, spin up a SSH session using that port and port forwarding
|
23
|
+
tunnel_env = @env.merge(
|
24
|
+
'TUNNEL_PORT' => remote_port, # Request a specific port
|
25
|
+
'TUNNEL_SIGNAL_OPEN' => '1' # Request signal when tunnel is up
|
26
|
+
)
|
27
|
+
|
28
|
+
tunnel_cmd = @cmd + [
|
29
|
+
'-L', "#{@local_port}:localhost:#{remote_port}",
|
30
|
+
'-o', 'SendEnv=TUNNEL_PORT',
|
31
|
+
'-o', 'SendEnv=TUNNEL_SIGNAL_OPEN'
|
32
|
+
]
|
33
|
+
|
34
|
+
r_pipe, w_pipe = IO.pipe
|
35
|
+
@pid = Process.spawn(tunnel_env, *tunnel_cmd, in: :close,
|
36
|
+
out: w_pipe,
|
37
|
+
err: err_fd)
|
38
|
+
|
39
|
+
# Wait for the tunnel to come up before returning. The other end
|
40
|
+
# will send a message on stdout to indicate that the tunnel is ready.
|
41
|
+
w_pipe.close
|
42
|
+
begin
|
43
|
+
r_pipe.readline
|
44
|
+
rescue EOFError
|
45
|
+
raise 'Server closed the tunnel'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
fail 'You must call #start before calling #stop' if @pid.nil?
|
51
|
+
Process.kill('HUP', @pid)
|
52
|
+
wait
|
53
|
+
end
|
54
|
+
|
55
|
+
def wait
|
56
|
+
Process.wait @pid
|
57
|
+
end
|
58
|
+
|
59
|
+
def port
|
60
|
+
fail 'You must call #start before calling #port!' if @local_port.nil?
|
61
|
+
@local_port
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def random_local_port
|
67
|
+
# Allocate a dummy server to discover an available port
|
68
|
+
dummy = TCPServer.new('127.0.0.1', 0)
|
69
|
+
port = dummy.addr[1]
|
70
|
+
dummy.close
|
71
|
+
port
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'term/ansicolor'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module Aptible
|
4
5
|
module CLI
|
@@ -50,15 +51,19 @@ module Aptible
|
|
50
51
|
option :environment
|
51
52
|
define_method 'db:dump' do |handle|
|
52
53
|
database = ensure_database(options.merge(db: handle))
|
53
|
-
|
54
|
+
with_postgres_tunnel(database) do |url|
|
55
|
+
filename = "#{handle}.dump"
|
56
|
+
say "Dumping to #{filename}"
|
57
|
+
`pg_dump #{url} > #{filename}`
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
desc 'db:execute HANDLE SQL_FILE', 'Executes sql against a database'
|
57
62
|
option :environment
|
58
63
|
define_method 'db:execute' do |handle, sql_path|
|
59
64
|
database = ensure_database(options.merge(db: handle))
|
60
|
-
|
61
|
-
say "Executing #{sql_path} against #{
|
65
|
+
with_postgres_tunnel(database) do |url|
|
66
|
+
say "Executing #{sql_path} against #{handle}"
|
62
67
|
`psql #{url} < #{sql_path}`
|
63
68
|
end
|
64
69
|
end
|
@@ -67,21 +72,31 @@ module Aptible
|
|
67
72
|
option :environment
|
68
73
|
option :port, type: :numeric
|
69
74
|
define_method 'db:tunnel' do |handle|
|
75
|
+
desired_port = Integer(options[:port] || 0)
|
70
76
|
database = ensure_database(options.merge(db: handle))
|
71
|
-
local_port = options[:port] || random_local_port
|
72
|
-
|
73
77
|
say 'Creating tunnel...', :green
|
74
|
-
say "Connect at #{local_url(database, local_port)}", :green
|
75
78
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
79
|
+
with_local_tunnel(database, desired_port) do |tunnel_helper|
|
80
|
+
port = tunnel_helper.port
|
81
|
+
say "Connect at #{local_url(database, port)}", :green
|
82
|
+
|
83
|
+
uri = URI(local_url(database, port))
|
84
|
+
db = uri.path.gsub(%r{^/}, '')
|
85
|
+
say 'Or, use the following arguments:', :green
|
86
|
+
say("* Host: #{uri.host}", :green)
|
87
|
+
say("* Port: #{uri.port}", :green)
|
88
|
+
say("* Username: #{uri.user}", :green) unless uri.user.empty?
|
89
|
+
say("* Password: #{uri.password}", :green)
|
90
|
+
say("* Database: #{db}", :green) unless db.empty?
|
91
|
+
|
92
|
+
say 'Connected. Ctrl-C to close connection.'
|
93
|
+
|
94
|
+
begin
|
95
|
+
tunnel_helper.wait
|
96
|
+
rescue Interrupt
|
97
|
+
say 'Closing tunnel'
|
98
|
+
end
|
99
|
+
end
|
85
100
|
end
|
86
101
|
|
87
102
|
desc 'db:deprovision HANDLE', 'Deprovision a database'
|
@@ -22,7 +22,7 @@ module Aptible
|
|
22
22
|
port = app.account.dumptruck_port
|
23
23
|
|
24
24
|
ENV['ACCESS_TOKEN'] = fetch_token
|
25
|
-
ENV['APTIBLE_APP'] = app.
|
25
|
+
ENV['APTIBLE_APP'] = app.href
|
26
26
|
ENV['APTIBLE_CLI_COMMAND'] = 'logs'
|
27
27
|
|
28
28
|
opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
|
@@ -20,7 +20,7 @@ module Aptible
|
|
20
20
|
port = app.account.dumptruck_port
|
21
21
|
|
22
22
|
set_env('ACCESS_TOKEN', fetch_token)
|
23
|
-
set_env('APTIBLE_APP', app.
|
23
|
+
set_env('APTIBLE_APP', app.href)
|
24
24
|
set_env('APTIBLE_CLI_COMMAND', 'ps')
|
25
25
|
|
26
26
|
opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
|
@@ -24,7 +24,7 @@ module Aptible
|
|
24
24
|
|
25
25
|
ENV['ACCESS_TOKEN'] = fetch_token
|
26
26
|
ENV['APTIBLE_COMMAND'] = command_from_args(*args)
|
27
|
-
ENV['APTIBLE_APP'] = app.
|
27
|
+
ENV['APTIBLE_APP'] = app.href
|
28
28
|
|
29
29
|
opts = options[:force_tty] ? '-t -t' : ''
|
30
30
|
opts << " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
|
data/lib/aptible/cli/version.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'climate_control'
|
3
|
+
|
4
|
+
describe Aptible::CLI::Helpers::Tunnel do
|
5
|
+
around do |example|
|
6
|
+
mocks_path = File.expand_path('../../../../mock', __FILE__)
|
7
|
+
path = "#{mocks_path}:#{ENV['PATH']}"
|
8
|
+
ClimateControl.modify PATH: path do
|
9
|
+
example.run
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'reuses the port it was given' do
|
14
|
+
helper = described_class.new({}, ['ssh_mock.rb'])
|
15
|
+
|
16
|
+
r, w = IO.pipe
|
17
|
+
helper.start(0, w)
|
18
|
+
helper.stop
|
19
|
+
|
20
|
+
expect(r.readline.chomp).to eq('6')
|
21
|
+
expect(r.readline.chomp).to eq('-L')
|
22
|
+
expect(r.readline.chomp).to match(/\d+:localhost:1234$/)
|
23
|
+
expect(r.readline.chomp).to eq('-o')
|
24
|
+
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_PORT')
|
25
|
+
expect(r.readline.chomp).to eq('-o')
|
26
|
+
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_SIGNAL_OPEN')
|
27
|
+
|
28
|
+
r.close
|
29
|
+
w.close
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'accepts a desired port' do
|
33
|
+
helper = described_class.new({}, ['ssh_mock.rb'])
|
34
|
+
r, w = IO.pipe
|
35
|
+
helper.start(5678, w)
|
36
|
+
helper.stop
|
37
|
+
|
38
|
+
expect(r.readline.chomp).to eq('6')
|
39
|
+
expect(r.readline.chomp).to eq('-L')
|
40
|
+
expect(r.readline.chomp).to eq('5678:localhost:1234')
|
41
|
+
expect(r.readline.chomp).to eq('-o')
|
42
|
+
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_PORT')
|
43
|
+
expect(r.readline.chomp).to eq('-o')
|
44
|
+
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_SIGNAL_OPEN')
|
45
|
+
|
46
|
+
r.close
|
47
|
+
w.close
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'captures and displays port discovery errors' do
|
51
|
+
helper = described_class.new({ 'FAIL_PORT' => '1' }, ['ssh_mock.rb'])
|
52
|
+
expect { helper.start }.to raise_error(/Something went wrong/)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'captures and displays tunnel errors' do
|
56
|
+
helper = described_class.new({ 'FAIL_TUNNEL' => '1' }, ['ssh_mock.rb'])
|
57
|
+
expect do
|
58
|
+
helper.start(0, File.open(File::NULL, 'w'))
|
59
|
+
end.to raise_error(/Server closed the tunnel/)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should fail if #port is called before #start' do
|
63
|
+
socat = described_class.new({}, [])
|
64
|
+
expect { socat.port }.to raise_error(/You must call #start/)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should fail if #stop is called before #start' do
|
68
|
+
socat = described_class.new({}, [])
|
69
|
+
expect { socat.stop }.to raise_error(/You must call #start/)
|
70
|
+
end
|
71
|
+
end
|
@@ -7,12 +7,13 @@ end
|
|
7
7
|
class Account < OpenStruct
|
8
8
|
end
|
9
9
|
|
10
|
+
class SocatHelperMock < OpenStruct
|
11
|
+
end
|
12
|
+
|
10
13
|
describe Aptible::CLI::Agent do
|
11
14
|
before { subject.stub(:ask) }
|
12
15
|
before { subject.stub(:save_token) }
|
13
16
|
before { subject.stub(:fetch_token) { double 'token' } }
|
14
|
-
before { subject.stub(:random_local_port) { 4242 } }
|
15
|
-
before { subject.stub(:establish_connection) }
|
16
17
|
|
17
18
|
let(:account) do
|
18
19
|
Account.new(bastion_host: 'localhost',
|
@@ -24,7 +25,14 @@ describe Aptible::CLI::Agent do
|
|
24
25
|
type: 'postgresql',
|
25
26
|
handle: 'foobar',
|
26
27
|
passphrase: 'password',
|
27
|
-
connection_url: 'postgresql://aptible:password@10.252.1.125:49158/db'
|
28
|
+
connection_url: 'postgresql://aptible:password@10.252.1.125:49158/db',
|
29
|
+
account: account
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:socat_helper) do
|
34
|
+
SocatHelperMock.new(
|
35
|
+
port: 4242
|
28
36
|
)
|
29
37
|
end
|
30
38
|
|
@@ -39,11 +47,14 @@ describe Aptible::CLI::Agent do
|
|
39
47
|
it 'should print a message about how to connect' do
|
40
48
|
allow(Aptible::Api::Database).to receive(:all) { [database] }
|
41
49
|
local_url = 'postgresql://aptible:password@127.0.0.1:4242/db'
|
50
|
+
|
51
|
+
expect(subject).to receive(:with_local_tunnel).with(database, 0)
|
52
|
+
.and_yield(socat_helper)
|
42
53
|
expect(subject).to receive(:say).with('Creating tunnel...', :green)
|
43
54
|
expect(subject).to receive(:say).with("Connect at #{local_url}", :green)
|
44
55
|
|
45
56
|
# db:tunnel should also explain each component of the URL:
|
46
|
-
expect(subject).to receive(:say).exactly(
|
57
|
+
expect(subject).to receive(:say).exactly(7).times
|
47
58
|
subject.send('db:tunnel', 'foobar')
|
48
59
|
end
|
49
60
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Emulate server behavior
|
4
|
+
|
5
|
+
if ENV['TUNNEL_PORT']
|
6
|
+
fail 'Something went wrong!' if ENV['FAIL_TUNNEL']
|
7
|
+
puts 'TUNNEL READY'
|
8
|
+
else
|
9
|
+
fail 'Something went wrong!' if ENV['FAIL_PORT']
|
10
|
+
puts 1234
|
11
|
+
end
|
12
|
+
|
13
|
+
# Log to stderr so we can collect in test
|
14
|
+
|
15
|
+
$stderr.puts ARGV.size
|
16
|
+
ARGV.each do |a|
|
17
|
+
$stderr.puts a
|
18
|
+
end
|
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.6.
|
4
|
+
version: 0.6.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frank Macreery
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aptible-api
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: climate_control
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
description: Aptible CLI
|
154
168
|
email:
|
155
169
|
- frank@macreery.com
|
@@ -176,6 +190,7 @@ files:
|
|
176
190
|
- lib/aptible/cli/helpers/environment.rb
|
177
191
|
- lib/aptible/cli/helpers/operation.rb
|
178
192
|
- lib/aptible/cli/helpers/token.rb
|
193
|
+
- lib/aptible/cli/helpers/tunnel.rb
|
179
194
|
- lib/aptible/cli/subcommands/apps.rb
|
180
195
|
- lib/aptible/cli/subcommands/config.rb
|
181
196
|
- lib/aptible/cli/subcommands/db.rb
|
@@ -188,11 +203,13 @@ files:
|
|
188
203
|
- lib/aptible/cli/version.rb
|
189
204
|
- spec/aptible/cli/agent_spec.rb
|
190
205
|
- spec/aptible/cli/helpers/handle_from_git_remote.rb
|
206
|
+
- spec/aptible/cli/helpers/tunnel_spec.rb
|
191
207
|
- spec/aptible/cli/subcommands/apps_spec.rb
|
192
208
|
- spec/aptible/cli/subcommands/db_spec.rb
|
193
209
|
- spec/aptible/cli/subcommands/domains_spec.rb
|
194
210
|
- spec/aptible/cli/subcommands/logs_spec.rb
|
195
211
|
- spec/aptible/cli/subcommands/ps_spec.rb
|
212
|
+
- spec/mock/ssh_mock.rb
|
196
213
|
- spec/spec_helper.rb
|
197
214
|
homepage: https://github.com/aptible/aptible-cli
|
198
215
|
licenses:
|
@@ -221,9 +238,11 @@ summary: Command-line interface for Aptible services
|
|
221
238
|
test_files:
|
222
239
|
- spec/aptible/cli/agent_spec.rb
|
223
240
|
- spec/aptible/cli/helpers/handle_from_git_remote.rb
|
241
|
+
- spec/aptible/cli/helpers/tunnel_spec.rb
|
224
242
|
- spec/aptible/cli/subcommands/apps_spec.rb
|
225
243
|
- spec/aptible/cli/subcommands/db_spec.rb
|
226
244
|
- spec/aptible/cli/subcommands/domains_spec.rb
|
227
245
|
- spec/aptible/cli/subcommands/logs_spec.rb
|
228
246
|
- spec/aptible/cli/subcommands/ps_spec.rb
|
247
|
+
- spec/mock/ssh_mock.rb
|
229
248
|
- spec/spec_helper.rb
|