aptible-api 0.9.13 → 0.9.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3f50076bb1d7c9974c01a2ead4175981911a9657
4
- data.tar.gz: 6a42f11ac1f0ea710c88ee9702f78816dbd08c67
3
+ metadata.gz: fca6144f0c1ad810abee436b30ed9b3e03df144f
4
+ data.tar.gz: 137b04d0be16b37453a23fe5d26b974178e7edc5
5
5
  SHA512:
6
- metadata.gz: d912b54ddad9f65164b1bb0bf1429c1d1b16bef07f3db3b4cf07fd4e4d7bfdbac99871523386c0099cfff06ecfe382520ec47f396eb1794f7c142b63ad8e476c
7
- data.tar.gz: f7ffaab3069a67be24ff36804ad70bfa9dfd63f3d2faf5570b46700bb1acbe0b53d639e85b1d1b785b242be77f374c60f2ffcec8070d7bda183ccb4096c36f4d
6
+ metadata.gz: c6fd7583ad12fc618073bf6fccc4c35bbc4b50836878c37837f166624d8eb47f5da9b79b5ab52ea15fe10657add1b64f4423290a3675395228c8ba992d628548
7
+ data.tar.gz: f577e098d4e40e56c00f18278e097e486524e5a4699b10519e7141624911274f0b07947f6a39082b18412d847b52a825ebff7b2f263070066821a431d6f7afcf
@@ -6,6 +6,8 @@ module Aptible
6
6
  belongs_to :account
7
7
  belongs_to :resource
8
8
 
9
+ has_many :ssh_portal_connections
10
+
9
11
  field :id
10
12
  field :type
11
13
  field :status
@@ -40,6 +42,62 @@ module Aptible
40
42
  def failed?
41
43
  status == 'failed'
42
44
  end
45
+
46
+ def with_ssh_cmd(private_key_file)
47
+ # We expect that the public key will be found next to the private key,
48
+ # which is also what SSH itself expects. If that's not the case, then
49
+ # we'll just fail. The Aptible CLI will always ensure credentials are
50
+ # set up properly (other consumers are of course responsible for doing
51
+ # the same!).
52
+ public_key_file = "#{private_key_file}.pub"
53
+
54
+ private_key = File.read(private_key_file)
55
+ public_key = File.read(public_key_file)
56
+
57
+ connection = create_ssh_portal_connection!(ssh_public_key: public_key)
58
+ certificate = connection.ssh_certificate_body
59
+
60
+ with_temporary_id(private_key, public_key, certificate) do |id_file|
61
+ cmd = [
62
+ 'ssh',
63
+ "#{connection.ssh_user}@#{account.bastion_host}",
64
+ '-p', account.ssh_portal_port.to_s
65
+ ] + ['-i', id_file]
66
+
67
+ # If we aren't allowed to create a pty, then we shouldn't try to
68
+ # allocate once, or we'll get an awkward error.
69
+ cmd << '-T' unless connection.ssh_pty
70
+
71
+ yield cmd, connection
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def with_temporary_id(private_key, public_key, certificate)
78
+ # Most versions of OpenSSH don't support specifying the SSH certificate
79
+ # to use when connecting, so we create a temporary directory with the
80
+ # credentials and the certificate. From a security perspective, the CLI
81
+ # makes sure to use an Aptible-CLI only SSH key to minimize exposure
82
+ # should we fail to clean out the temporary directory.
83
+ Dir.mktmpdir do |dir|
84
+ private_key_file = File.join(dir, 'id_rsa')
85
+ public_key_file = "#{private_key_file}.pub"
86
+ certificate_file = "#{private_key_file}-cert.pub"
87
+
88
+ pairs = [
89
+ [private_key, private_key_file],
90
+ [public_key, public_key_file],
91
+ [certificate, certificate_file]
92
+ ]
93
+
94
+ pairs.each do |contents, file|
95
+ File.open(file, 'w', 0o600) { |f| f.write(contents) }
96
+ end
97
+
98
+ yield private_key_file
99
+ end
100
+ end
43
101
  end
44
102
  end
45
103
  end
@@ -30,3 +30,4 @@ require 'aptible/api/permission'
30
30
  require 'aptible/api/release'
31
31
  require 'aptible/api/service'
32
32
  require 'aptible/api/vhost'
33
+ require 'aptible/api/ssh_portal_connection'
@@ -0,0 +1,11 @@
1
+ module Aptible
2
+ module Api
3
+ class SshPortalConnection < Resource
4
+ belongs_to :account
5
+ belongs_to :log_drain
6
+ belongs_to :operation
7
+
8
+ field :action
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module Api
3
- VERSION = '0.9.13'.freeze
3
+ VERSION = '0.9.14'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aptible::Api::Operation do
4
+ describe '#with_ssh_cmd' do
5
+ shared_examples '#with_ssh_cmd examples' do
6
+ let(:account) do
7
+ Aptible::Api::Account.new.tap do |account|
8
+ account.stub(
9
+ bastion_host: 'foo-bastion.com',
10
+ ssh_portal_port: 1022
11
+ )
12
+ end
13
+ end
14
+
15
+ let(:ssh_portal_connection) do
16
+ Aptible::Api::SshPortalConnection.new.tap do |connection|
17
+ connection.stub(
18
+ ssh_user: 'foo-user',
19
+ ssh_certificate_body: 'some certificate',
20
+ ssh_pty: ssh_pty
21
+ )
22
+ end
23
+ end
24
+
25
+ subject do
26
+ described_class.new.tap do |operation|
27
+ operation.stub(account: account)
28
+ end
29
+ end
30
+
31
+ let!(:work_dir) { Dir.mktmpdir }
32
+ after { FileUtils.remove_entry work_dir }
33
+
34
+ let(:private_key_file) { File.join(work_dir, 'id_rsa') }
35
+ let(:public_key_file) { "#{private_key_file}.pub" }
36
+
37
+ before do
38
+ File.open(private_key_file, 'w') { |f| f.write('some private key') }
39
+ File.open(public_key_file, 'w') { |f| f.write('some public key') }
40
+ end
41
+
42
+ it 'yields usable SSH connection arguments' do
43
+ expect(subject).to receive(:create_ssh_portal_connection!)
44
+ .with(ssh_public_key: 'some public key')
45
+ .and_return(ssh_portal_connection)
46
+
47
+ has_yielded = false
48
+
49
+ subject.with_ssh_cmd(private_key_file) do |cmd, connection|
50
+ _, dest, _, port, _, id_file, = cmd
51
+
52
+ expect(dest).to eq('foo-user@foo-bastion.com')
53
+ expect(port).to eq('1022')
54
+ expect(File.read(id_file)).to eq('some private key')
55
+ expect(File.read("#{id_file}.pub")).to eq('some public key')
56
+ expect(File.read("#{id_file}-cert.pub")).to eq('some certificate')
57
+
58
+ expect(File.readable?(id_file)).to be_truthy
59
+ expect(File.writable?(id_file)).to be_truthy
60
+
61
+ expect(File.world_readable?(id_file)).to be_falsey
62
+ expect(File.world_writable?(id_file)).to be_falsey
63
+
64
+ expect(connection).to be(ssh_portal_connection)
65
+
66
+ if ssh_pty
67
+ expect(cmd).not_to include('-T')
68
+ else
69
+ expect(cmd.last).to eq('-T')
70
+ end
71
+
72
+ has_yielded = true
73
+ end
74
+
75
+ expect(has_yielded).to be_truthy
76
+ end
77
+ end
78
+
79
+ context 'with a PTY' do
80
+ let(:ssh_pty) { true }
81
+ include_examples '#with_ssh_cmd examples'
82
+ end
83
+
84
+ context 'without a PTY' do
85
+ let(:ssh_pty) { false }
86
+ include_examples '#with_ssh_cmd examples'
87
+ end
88
+ end
89
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aptible-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.13
4
+ version: 0.9.14
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-30 00:00:00.000000000 Z
11
+ date: 2016-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-resource
@@ -198,9 +198,11 @@ files:
198
198
  - lib/aptible/api/release.rb
199
199
  - lib/aptible/api/resource.rb
200
200
  - lib/aptible/api/service.rb
201
+ - lib/aptible/api/ssh_portal_connection.rb
201
202
  - lib/aptible/api/version.rb
202
203
  - lib/aptible/api/vhost.rb
203
204
  - spec/aptible/api/agent_spec.rb
205
+ - spec/aptible/api/operation_spec.rb
204
206
  - spec/aptible/api/resource_spec.rb
205
207
  - spec/aptible/api_spec.rb
206
208
  - spec/shared/with_env.rb
@@ -231,6 +233,7 @@ specification_version: 4
231
233
  summary: Ruby client for api.aptible.com
232
234
  test_files:
233
235
  - spec/aptible/api/agent_spec.rb
236
+ - spec/aptible/api/operation_spec.rb
234
237
  - spec/aptible/api/resource_spec.rb
235
238
  - spec/aptible/api_spec.rb
236
239
  - spec/shared/with_env.rb