aptible-cli 0.7.1 → 0.7.2
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/.rubocop.yml +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -0
- data/README.md +5 -1
- data/lib/aptible/cli/agent.rb +1 -1
- data/lib/aptible/cli/helpers/app.rb +5 -1
- data/lib/aptible/cli/helpers/database.rb +18 -18
- data/lib/aptible/cli/helpers/environment.rb +0 -1
- data/lib/aptible/cli/helpers/operation.rb +13 -9
- data/lib/aptible/cli/helpers/ssh.rb +33 -0
- data/lib/aptible/cli/helpers/token.rb +1 -1
- data/lib/aptible/cli/helpers/tunnel.rb +24 -14
- data/lib/aptible/cli/subcommands/logs.rb +7 -6
- data/lib/aptible/cli/subcommands/ps.rb +9 -9
- data/lib/aptible/cli/subcommands/ssh.rb +10 -7
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/agent_spec.rb +11 -0
- data/spec/aptible/cli/helpers/tunnel_spec.rb +24 -36
- data/spec/aptible/cli/subcommands/ps_spec.rb +13 -7
- data/spec/mock/git +2 -0
- data/spec/mock/ssh +22 -0
- data/spec/mock/ssh_mock.rb +8 -4
- data/spec/shared/mock_ssh_context.rb +30 -0
- metadata +10 -4
- data/lib/aptible/cli/helpers/env.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9eee8bccf78fd7a0814d5fb48e92c9c31aaef356
|
4
|
+
data.tar.gz: 80d7cf361bba1d14c1c50ab1f832de3c46533e72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c51adcf6c82f63b5129674a82070235d6ab079c7ed91fc8b02771ba81f8e243a459d39cf80135687797a6576320942a7ec767a5bec78bfc5155bfacdfca5caf
|
7
|
+
data.tar.gz: 2c5ccc49276560307c75d5dff6b6504d87b6f77a4bf9b820910c37900b65059e341ef2676e888870ffca0dbc9ae503ad82d328be7d93f16866b45605330cf78a
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -10,13 +10,17 @@ Command-line interface for Aptible services.
|
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
13
|
+
**NOTE: To install the `aptible` tool as a system-level binary, Aptible
|
14
|
+
recommends you install the
|
15
|
+
[Aptible Toolbelt](https://support.aptible.com/toolbelt/)**, which is faster
|
16
|
+
and more robust.
|
17
|
+
|
13
18
|
Add the following line to your application's Gemfile.
|
14
19
|
|
15
20
|
gem 'aptible-cli'
|
16
21
|
|
17
22
|
And then run `bundle install`.
|
18
23
|
|
19
|
-
*NOTE: To install the `aptible` tool as a system-level binary, consider using the [aptible-toolbelt gem](https://github.com/aptible/aptible-toolbelt), which is performance-optimized through dependency pinning.*
|
20
24
|
|
21
25
|
## Usage
|
22
26
|
|
data/lib/aptible/cli/agent.rb
CHANGED
@@ -3,12 +3,12 @@ require 'thor'
|
|
3
3
|
require 'json'
|
4
4
|
require 'chronic_duration'
|
5
5
|
|
6
|
+
require_relative 'helpers/ssh'
|
6
7
|
require_relative 'helpers/token'
|
7
8
|
require_relative 'helpers/operation'
|
8
9
|
require_relative 'helpers/environment'
|
9
10
|
require_relative 'helpers/app'
|
10
11
|
require_relative 'helpers/database'
|
11
|
-
require_relative 'helpers/env'
|
12
12
|
require_relative 'helpers/tunnel'
|
13
13
|
|
14
14
|
require_relative 'subcommands/apps'
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'aptible/api'
|
2
|
-
|
2
|
+
|
3
|
+
# Avoid requiring the git gem upfront, since it'll fail to load if git isn't
|
4
|
+
# available, whereas we're able to gracefully handle that by requiring the
|
5
|
+
# --app and / or --environment flags.
|
6
|
+
autoload :Git, 'git'
|
3
7
|
|
4
8
|
module Aptible
|
5
9
|
module CLI
|
@@ -6,6 +6,7 @@ module Aptible
|
|
6
6
|
module Database
|
7
7
|
include Helpers::Token
|
8
8
|
include Helpers::Environment
|
9
|
+
include Helpers::Ssh
|
9
10
|
|
10
11
|
def ensure_database(options = {})
|
11
12
|
db_handle = options[:db]
|
@@ -54,16 +55,12 @@ module Aptible
|
|
54
55
|
# Creates a local tunnel and yields the helper
|
55
56
|
|
56
57
|
def with_local_tunnel(database, port = 0)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
tunnel_helper.start(port)
|
64
|
-
yield tunnel_helper
|
65
|
-
tunnel_helper.stop
|
66
|
-
end
|
58
|
+
tunnel_helper = Helpers::Tunnel.new(ssh_env(database),
|
59
|
+
ssh_args(database))
|
60
|
+
|
61
|
+
tunnel_helper.start(port)
|
62
|
+
yield tunnel_helper if block_given?
|
63
|
+
tunnel_helper.stop
|
67
64
|
end
|
68
65
|
|
69
66
|
# Creates a local PG tunnel and yields the url to it
|
@@ -88,15 +85,18 @@ module Aptible
|
|
88
85
|
"127.0.0.1:#{local_port}#{uri.path}"
|
89
86
|
end
|
90
87
|
|
88
|
+
def ssh_env(database)
|
89
|
+
{
|
90
|
+
'ACCESS_TOKEN' => fetch_token,
|
91
|
+
'APTIBLE_DATABASE' => database.href
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
91
95
|
def ssh_args(database)
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
'-o', 'SendEnv=ACCESS_TOKEN',
|
97
|
-
'-o', 'StrictHostKeyChecking=no',
|
98
|
-
'-o', 'UserKnownHostsFile=/dev/null',
|
99
|
-
'-p', port.to_s, "root@#{host}"]
|
96
|
+
broadwayjoe_ssh_command(database.account) + [
|
97
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
98
|
+
'-o', 'SendEnv=APTIBLE_DATABASE'
|
99
|
+
]
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -4,6 +4,8 @@ module Aptible
|
|
4
4
|
module CLI
|
5
5
|
module Helpers
|
6
6
|
module Operation
|
7
|
+
include Helpers::Ssh
|
8
|
+
|
7
9
|
POLL_INTERVAL = 1
|
8
10
|
|
9
11
|
def poll_for_success(operation)
|
@@ -21,20 +23,22 @@ module Aptible
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def attach_to_operation_logs(operation)
|
24
|
-
|
25
|
-
|
26
|
+
ENV['ACCESS_TOKEN'] = fetch_token
|
27
|
+
ENV['APTIBLE_OPERATION'] = operation.id.to_s
|
28
|
+
ENV['APTIBLE_CLI_COMMAND'] = 'oplog'
|
29
|
+
|
30
|
+
cmd = dumptruck_ssh_command(operation.resource.account) + [
|
31
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
32
|
+
'-o', 'SendEnv=APTIBLE_OPERATION',
|
33
|
+
'-o', 'SendEnv=APTIBLE_CLI_COMMAND'
|
34
|
+
]
|
26
35
|
|
27
|
-
|
28
|
-
set_env('APTIBLE_OPERATION', operation.id.to_s)
|
29
|
-
set_env('APTIBLE_CLI_COMMAND', 'oplog')
|
36
|
+
success = Kernel.system(*cmd)
|
30
37
|
|
31
|
-
opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
|
32
|
-
'-o UserKnownHostsFile=/dev/null -o LogLevel=quiet'
|
33
|
-
result = Kernel.system "ssh #{opts} -p #{port} root@#{host}"
|
34
38
|
# If Dumptruck is down, fall back to polling for success. If the
|
35
39
|
# operation failed, poll_for_success will immediately fall through to
|
36
40
|
# the error message.
|
37
|
-
poll_for_success(operation) unless
|
41
|
+
poll_for_success(operation) unless success
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Aptible
|
2
|
+
module CLI
|
3
|
+
module Helpers
|
4
|
+
module Ssh
|
5
|
+
def dumptruck_ssh_command(account)
|
6
|
+
base_ssh_command(account, :dumptruck_port)
|
7
|
+
end
|
8
|
+
|
9
|
+
def broadwayjoe_ssh_command(account)
|
10
|
+
base_ssh_command(account, :bastion_port)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def base_ssh_command(account, port_method)
|
16
|
+
log_level = ENV['APTIBLE_SSH_VERBOSE'] ? 'VERBOSE' : 'ERROR'
|
17
|
+
|
18
|
+
[
|
19
|
+
'ssh',
|
20
|
+
"root@#{account.bastion_host}",
|
21
|
+
'-p', account.public_send(port_method).to_s,
|
22
|
+
'-o', 'StrictHostKeyChecking=no',
|
23
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
24
|
+
'-o', 'TCPKeepAlive=yes',
|
25
|
+
'-o', 'KeepAlive=yes',
|
26
|
+
'-o', 'ServerAliveInterval=60',
|
27
|
+
'-o', "LogLevel=#{log_level}"
|
28
|
+
]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,44 +5,54 @@ module Aptible
|
|
5
5
|
module CLI
|
6
6
|
module Helpers
|
7
7
|
class Tunnel
|
8
|
-
def initialize(env,
|
8
|
+
def initialize(env, ssh_cmd)
|
9
9
|
@env = env
|
10
|
-
@
|
10
|
+
@ssh_cmd = ssh_cmd
|
11
11
|
end
|
12
12
|
|
13
|
-
def start(desired_port = 0
|
13
|
+
def start(desired_port = 0)
|
14
14
|
@local_port = desired_port
|
15
|
-
@local_port = random_local_port if @local_port
|
15
|
+
@local_port = random_local_port if @local_port.zero?
|
16
16
|
|
17
17
|
# First, grab a remote port
|
18
|
-
out, err, status = Open3.capture3(@env, *@
|
18
|
+
out, err, status = Open3.capture3(@env, *@ssh_cmd)
|
19
19
|
fail "Failed to request remote port: #{err}" unless status.success?
|
20
20
|
remote_port = out.chomp
|
21
21
|
|
22
|
-
# Then, spin up a SSH session using that port and port forwarding
|
22
|
+
# Then, spin up a SSH session using that port and port forwarding.
|
23
|
+
# Pass ExitOnForwardFailure to ensure nothing else can be listening
|
24
|
+
# on this port (thanks to Diego Argueta for reporting this issue).
|
23
25
|
tunnel_env = @env.merge(
|
24
26
|
'TUNNEL_PORT' => remote_port, # Request a specific port
|
25
27
|
'TUNNEL_SIGNAL_OPEN' => '1' # Request signal when tunnel is up
|
26
28
|
)
|
27
29
|
|
28
|
-
|
30
|
+
# TODO: Dynamically compose SendEnv from tunnel_env
|
31
|
+
tunnel_cmd = @ssh_cmd + [
|
29
32
|
'-L', "#{@local_port}:localhost:#{remote_port}",
|
30
33
|
'-o', 'SendEnv=TUNNEL_PORT',
|
31
|
-
'-o', 'SendEnv=TUNNEL_SIGNAL_OPEN'
|
34
|
+
'-o', 'SendEnv=TUNNEL_SIGNAL_OPEN',
|
35
|
+
'-o', 'ExitOnForwardFailure=yes'
|
32
36
|
]
|
33
37
|
|
34
|
-
|
38
|
+
out_read, out_write = IO.pipe
|
39
|
+
err_read, err_write = IO.pipe
|
35
40
|
@pid = Process.spawn(tunnel_env, *tunnel_cmd, in: :close,
|
36
|
-
out:
|
37
|
-
err:
|
41
|
+
out: out_write,
|
42
|
+
err: err_write)
|
38
43
|
|
39
44
|
# Wait for the tunnel to come up before returning. The other end
|
40
45
|
# will send a message on stdout to indicate that the tunnel is ready.
|
41
|
-
|
46
|
+
[out_write, err_write].map(&:close)
|
42
47
|
begin
|
43
|
-
|
48
|
+
out_read.readline
|
44
49
|
rescue EOFError
|
45
|
-
|
50
|
+
stop
|
51
|
+
e = 'Tunnel did not come up, is something else listening on port ' \
|
52
|
+
"#{@local_port}?\n#{err_read.read}"
|
53
|
+
raise e
|
54
|
+
ensure
|
55
|
+
[out_read, err_read].map(&:close)
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
@@ -18,16 +18,17 @@ module Aptible
|
|
18
18
|
"Have you deployed #{app.handle} yet?"
|
19
19
|
end
|
20
20
|
|
21
|
-
host = app.account.bastion_host
|
22
|
-
port = app.account.dumptruck_port
|
23
|
-
|
24
21
|
ENV['ACCESS_TOKEN'] = fetch_token
|
25
22
|
ENV['APTIBLE_APP'] = app.href
|
26
23
|
ENV['APTIBLE_CLI_COMMAND'] = 'logs'
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
cmd = dumptruck_ssh_command(app.account) + [
|
26
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
27
|
+
'-o', 'SendEnv=APTIBLE_APP',
|
28
|
+
'-o', 'SendEnv=APTIBLE_CLI_COMMAND'
|
29
|
+
]
|
30
|
+
|
31
|
+
Kernel.exec(*cmd)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -8,7 +8,6 @@ module Aptible
|
|
8
8
|
thor.class_eval do
|
9
9
|
include Helpers::Operation
|
10
10
|
include Helpers::App
|
11
|
-
include Helpers::Env
|
12
11
|
|
13
12
|
desc 'ps', 'Display running processes for an app - DEPRECATED'
|
14
13
|
app_options
|
@@ -16,16 +15,17 @@ module Aptible
|
|
16
15
|
app = ensure_app(options)
|
17
16
|
deprecated('This command is deprecated on Aptible v2 stacks.')
|
18
17
|
|
19
|
-
|
20
|
-
|
18
|
+
ENV['ACCESS_TOKEN'] = fetch_token
|
19
|
+
ENV['APTIBLE_APP'] = app.href
|
20
|
+
ENV['APTIBLE_CLI_COMMAND'] = 'ps'
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
cmd = dumptruck_ssh_command(app.account) + [
|
23
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
24
|
+
'-o', 'SendEnv=APTIBLE_APP',
|
25
|
+
'-o', 'SendEnv=APTIBLE_CLI_COMMAND'
|
26
|
+
]
|
25
27
|
|
26
|
-
|
27
|
-
'-o UserKnownHostsFile=/dev/null'
|
28
|
-
Kernel.exec "ssh #{opts} -p #{port} root@#{host}"
|
28
|
+
Kernel.exec(*cmd)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -8,6 +8,7 @@ module Aptible
|
|
8
8
|
thor.class_eval do
|
9
9
|
include Helpers::Operation
|
10
10
|
include Helpers::App
|
11
|
+
include Helpers::Ssh
|
11
12
|
|
12
13
|
desc 'ssh [COMMAND]', 'Run a command against an app'
|
13
14
|
long_desc <<-LONGDESC
|
@@ -19,17 +20,19 @@ module Aptible
|
|
19
20
|
option :force_tty, type: :boolean
|
20
21
|
def ssh(*args)
|
21
22
|
app = ensure_app(options)
|
22
|
-
host = app.account.bastion_host
|
23
|
-
port = app.account.bastion_port
|
24
23
|
|
25
24
|
ENV['ACCESS_TOKEN'] = fetch_token
|
26
|
-
ENV['APTIBLE_COMMAND'] = command_from_args(*args)
|
27
25
|
ENV['APTIBLE_APP'] = app.href
|
26
|
+
ENV['APTIBLE_COMMAND'] = command_from_args(*args)
|
27
|
+
|
28
|
+
cmd = broadwayjoe_ssh_command(app.account) + [
|
29
|
+
'-o', 'SendEnv=ACCESS_TOKEN',
|
30
|
+
'-o', 'SendEnv=APTIBLE_APP',
|
31
|
+
'-o', 'SendEnv=APTIBLE_COMMAND'
|
32
|
+
]
|
33
|
+
cmd << '-tt' if options[:force_tty]
|
28
34
|
|
29
|
-
|
30
|
-
opts << " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
|
31
|
-
'-o UserKnownHostsFile=/dev/null'
|
32
|
-
Kernel.exec "ssh #{opts} -p #{port} root@#{host}"
|
35
|
+
Kernel.exec(*cmd)
|
33
36
|
end
|
34
37
|
|
35
38
|
private
|
data/lib/aptible/cli/version.rb
CHANGED
@@ -157,4 +157,15 @@ describe Aptible::CLI::Agent do
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
160
|
+
|
161
|
+
context 'load' do
|
162
|
+
it 'loads without git' do
|
163
|
+
mocks = File.expand_path('../../../mock', __FILE__)
|
164
|
+
bins = File.expand_path('../../../../bin', __FILE__)
|
165
|
+
ClimateControl.modify PATH: [mocks, bins, ENV['PATH']].join(':') do
|
166
|
+
_, _, status = Open3.capture3('aptible version')
|
167
|
+
expect(status).to eq(0)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
160
171
|
end
|
@@ -1,61 +1,49 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Aptible::CLI::Helpers::Tunnel do
|
4
|
-
|
5
|
-
mocks_path = File.expand_path('../../../../mock', __FILE__)
|
6
|
-
path = "#{mocks_path}:#{ENV['PATH']}"
|
7
|
-
ClimateControl.modify PATH: path do
|
8
|
-
example.run
|
9
|
-
end
|
10
|
-
end
|
4
|
+
include_context 'mock ssh'
|
11
5
|
|
12
|
-
it '
|
6
|
+
it 'forwards traffic to the remote port given by the server (1234)' do
|
13
7
|
helper = described_class.new({}, ['ssh_mock.rb'])
|
14
8
|
|
15
|
-
|
16
|
-
helper.start(0, w)
|
9
|
+
helper.start(0)
|
17
10
|
helper.stop
|
18
11
|
|
19
|
-
|
20
|
-
expect(
|
21
|
-
|
22
|
-
expect(
|
23
|
-
expect(
|
24
|
-
expect(
|
25
|
-
expect(
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
mock_argv = read_mock_argv
|
13
|
+
expect(mock_argv.size).to eq(8)
|
14
|
+
|
15
|
+
expect(mock_argv.shift).to eq('-L')
|
16
|
+
expect(mock_argv.shift).to match(/\d+:localhost:1234$/)
|
17
|
+
expect(mock_argv.shift).to eq('-o')
|
18
|
+
expect(mock_argv.shift).to eq('SendEnv=TUNNEL_PORT')
|
19
|
+
expect(mock_argv.shift).to eq('-o')
|
20
|
+
expect(mock_argv.shift).to eq('SendEnv=TUNNEL_SIGNAL_OPEN')
|
21
|
+
expect(mock_argv.shift).to eq('-o')
|
22
|
+
expect(mock_argv.shift).to eq('ExitOnForwardFailure=yes')
|
29
23
|
end
|
30
24
|
|
31
|
-
it 'accepts a desired port' do
|
25
|
+
it 'accepts a desired local port' do
|
32
26
|
helper = described_class.new({}, ['ssh_mock.rb'])
|
33
|
-
|
34
|
-
helper.start(5678, w)
|
27
|
+
helper.start(5678)
|
35
28
|
helper.stop
|
36
29
|
|
37
|
-
|
38
|
-
expect(
|
39
|
-
expect(r.readline.chomp).to eq('5678:localhost:1234')
|
40
|
-
expect(r.readline.chomp).to eq('-o')
|
41
|
-
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_PORT')
|
42
|
-
expect(r.readline.chomp).to eq('-o')
|
43
|
-
expect(r.readline.chomp).to eq('SendEnv=TUNNEL_SIGNAL_OPEN')
|
30
|
+
mock_argv = read_mock_argv
|
31
|
+
expect(mock_argv.size).to eq(8)
|
44
32
|
|
45
|
-
|
46
|
-
|
33
|
+
expect(mock_argv.shift).to eq('-L')
|
34
|
+
expect(mock_argv.shift).to eq('5678:localhost:1234')
|
47
35
|
end
|
48
36
|
|
49
37
|
it 'captures and displays port discovery errors' do
|
50
38
|
helper = described_class.new({ 'FAIL_PORT' => '1' }, ['ssh_mock.rb'])
|
51
|
-
expect { helper.start }
|
39
|
+
expect { helper.start }
|
40
|
+
.to raise_error(/Failed to request.*Something went wrong/m)
|
52
41
|
end
|
53
42
|
|
54
43
|
it 'captures and displays tunnel errors' do
|
55
44
|
helper = described_class.new({ 'FAIL_TUNNEL' => '1' }, ['ssh_mock.rb'])
|
56
|
-
expect
|
57
|
-
|
58
|
-
end.to raise_error(/Server closed the tunnel/)
|
45
|
+
expect { helper.start(0) }
|
46
|
+
.to raise_error(/Tunnel did not come up.*Something went wrong/m)
|
59
47
|
end
|
60
48
|
|
61
49
|
it 'should fail if #port is called before #start' do
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Aptible::CLI::Agent do
|
4
|
+
include_context 'mock ssh'
|
5
|
+
|
4
6
|
let(:account) do
|
5
7
|
Fabricate(:account, bastion_host: 'bastion.com', dumptruck_port: 45022)
|
6
8
|
end
|
@@ -10,21 +12,25 @@ describe Aptible::CLI::Agent do
|
|
10
12
|
before { subject.stub(:save_token) }
|
11
13
|
before { subject.stub(:fetch_token) { double 'token' } }
|
12
14
|
before { subject.stub(:ensure_app) { app } }
|
13
|
-
|
14
|
-
before
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(Kernel).to receive(:exec) do |*args|
|
18
|
+
Kernel.system(*args)
|
19
|
+
end
|
20
|
+
end
|
15
21
|
|
16
22
|
describe '#ps' do
|
17
23
|
it 'should set ENV["APTIBLE_CLI_COMMAND"]' do
|
18
|
-
expect(subject).to receive(:set_env).with('APTIBLE_CLI_COMMAND', 'ps')
|
19
24
|
subject.send('ps')
|
25
|
+
expect(read_mock_env['APTIBLE_CLI_COMMAND']).to eq('ps')
|
20
26
|
end
|
21
27
|
|
22
28
|
it 'should construct a proper SSH call' do
|
23
|
-
expect(Kernel).to receive(:exec) do |*args|
|
24
|
-
cmd = args.first
|
25
|
-
expect(cmd).to match(/ssh.*-p 45022 root@bastion.com/)
|
26
|
-
end
|
27
29
|
subject.send('ps')
|
30
|
+
|
31
|
+
mock_argv = read_mock_argv
|
32
|
+
expect(mock_argv).to include('root@bastion.com')
|
33
|
+
expect(mock_argv).to include('45022')
|
28
34
|
end
|
29
35
|
end
|
30
36
|
end
|
data/spec/mock/git
ADDED
data/spec/mock/ssh
ADDED
@@ -0,0 +1,22 @@
|
|
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 SSH_MOCK_OUTFILE
|
14
|
+
require 'json'
|
15
|
+
|
16
|
+
File.open(ENV.fetch('SSH_MOCK_OUTFILE'), 'w') do |f|
|
17
|
+
f.write({
|
18
|
+
'argc' => ARGV.size,
|
19
|
+
'argv' => ARGV,
|
20
|
+
'env' => ENV.to_hash
|
21
|
+
}.to_json)
|
22
|
+
end
|
data/spec/mock/ssh_mock.rb
CHANGED
@@ -10,9 +10,13 @@ else
|
|
10
10
|
puts 1234
|
11
11
|
end
|
12
12
|
|
13
|
-
# Log to
|
13
|
+
# Log to SSH_MOCK_OUTFILE
|
14
|
+
require 'json'
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
File.open(ENV.fetch('SSH_MOCK_OUTFILE'), 'w') do |f|
|
17
|
+
f.write({
|
18
|
+
'argc' => ARGV.size,
|
19
|
+
'argv' => ARGV,
|
20
|
+
'env' => ENV.to_hash
|
21
|
+
}.to_json)
|
18
22
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
shared_context 'mock ssh' do
|
2
|
+
let(:ssh_mock_outfile) { Tempfile.new('tunnel_spec') }
|
3
|
+
|
4
|
+
after do
|
5
|
+
ssh_mock_outfile.close
|
6
|
+
ssh_mock_outfile.unlink
|
7
|
+
end
|
8
|
+
|
9
|
+
around do |example|
|
10
|
+
mocks_path = File.expand_path('../../mock', __FILE__)
|
11
|
+
env = {
|
12
|
+
PATH: "#{mocks_path}:#{ENV['PATH']}",
|
13
|
+
SSH_MOCK_OUTFILE: ssh_mock_outfile.path
|
14
|
+
}
|
15
|
+
|
16
|
+
ClimateControl.modify(env) { example.run }
|
17
|
+
end
|
18
|
+
|
19
|
+
def read_mock_argv
|
20
|
+
File.open(ssh_mock_outfile) do |f|
|
21
|
+
return JSON.load(f.read).fetch('argv')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_mock_env
|
26
|
+
File.open(ssh_mock_outfile) do |f|
|
27
|
+
return JSON.load(f.read).fetch('env')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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.7.
|
4
|
+
version: 0.7.2
|
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-
|
11
|
+
date: 2016-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aptible-api
|
@@ -229,9 +229,9 @@ files:
|
|
229
229
|
- lib/aptible/cli/agent.rb
|
230
230
|
- lib/aptible/cli/helpers/app.rb
|
231
231
|
- lib/aptible/cli/helpers/database.rb
|
232
|
-
- lib/aptible/cli/helpers/env.rb
|
233
232
|
- lib/aptible/cli/helpers/environment.rb
|
234
233
|
- lib/aptible/cli/helpers/operation.rb
|
234
|
+
- lib/aptible/cli/helpers/ssh.rb
|
235
235
|
- lib/aptible/cli/helpers/token.rb
|
236
236
|
- lib/aptible/cli/helpers/tunnel.rb
|
237
237
|
- lib/aptible/cli/subcommands/apps.rb
|
@@ -263,7 +263,10 @@ files:
|
|
263
263
|
- spec/fabricators/operation_fabricator.rb
|
264
264
|
- spec/fabricators/service_fabricator.rb
|
265
265
|
- spec/fabricators/vhost_fabricator.rb
|
266
|
+
- spec/mock/git
|
267
|
+
- spec/mock/ssh
|
266
268
|
- spec/mock/ssh_mock.rb
|
269
|
+
- spec/shared/mock_ssh_context.rb
|
267
270
|
- spec/spec_helper.rb
|
268
271
|
homepage: https://github.com/aptible/aptible-cli
|
269
272
|
licenses:
|
@@ -285,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
285
288
|
version: '0'
|
286
289
|
requirements: []
|
287
290
|
rubyforge_project:
|
288
|
-
rubygems_version: 2.
|
291
|
+
rubygems_version: 2.4.5.1
|
289
292
|
signing_key:
|
290
293
|
specification_version: 4
|
291
294
|
summary: Command-line interface for Aptible services
|
@@ -308,5 +311,8 @@ test_files:
|
|
308
311
|
- spec/fabricators/operation_fabricator.rb
|
309
312
|
- spec/fabricators/service_fabricator.rb
|
310
313
|
- spec/fabricators/vhost_fabricator.rb
|
314
|
+
- spec/mock/git
|
315
|
+
- spec/mock/ssh
|
311
316
|
- spec/mock/ssh_mock.rb
|
317
|
+
- spec/shared/mock_ssh_context.rb
|
312
318
|
- spec/spec_helper.rb
|