aptible-cli 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|