aptible-cli 0.19.3 → 0.19.6
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/.travis.yml +3 -1
- data/README.md +70 -64
- data/aptible-cli.gemspec +1 -0
- data/lib/aptible/cli/agent.rb +1 -0
- data/lib/aptible/cli/helpers/database.rb +13 -0
- data/lib/aptible/cli/helpers/operation.rb +41 -0
- data/lib/aptible/cli/helpers/s3_log_helpers.rb +225 -0
- data/lib/aptible/cli/subcommands/apps.rb +23 -0
- data/lib/aptible/cli/subcommands/db.rb +20 -0
- data/lib/aptible/cli/subcommands/environment.rb +19 -0
- data/lib/aptible/cli/subcommands/logs.rb +145 -0
- data/lib/aptible/cli/subcommands/metric_drain.rb +2 -1
- data/lib/aptible/cli/subcommands/operation.rb +33 -0
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/helpers/database_spec.rb +36 -0
- data/spec/aptible/cli/helpers/s3_log_helpers_spec.rb +334 -0
- data/spec/aptible/cli/subcommands/apps_spec.rb +41 -0
- data/spec/aptible/cli/subcommands/db_spec.rb +38 -0
- data/spec/aptible/cli/subcommands/environment_spec.rb +67 -32
- data/spec/aptible/cli/subcommands/logs_spec.rb +133 -0
- data/spec/aptible/cli/subcommands/metric_drain_spec.rb +1 -1
- data/spec/aptible/cli/subcommands/operation_spec.rb +172 -0
- data/spec/fabricators/operation_fabricator.rb +6 -1
- metadata +25 -6
|
@@ -197,6 +197,47 @@ describe Aptible::CLI::Agent do
|
|
|
197
197
|
end
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
+
describe '#apps:rename' do
|
|
201
|
+
before do
|
|
202
|
+
allow(Aptible::Api::App).to receive(:all) { [app] }
|
|
203
|
+
allow(Aptible::Api::Account).to receive(:all) { [account] }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
before(:each) do
|
|
207
|
+
allow(subject).to receive(:options)
|
|
208
|
+
.and_return(environment: account.handle)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
context 'with environment and app' do
|
|
212
|
+
it 'should rename properly' do
|
|
213
|
+
expect(app).to receive(:update!)
|
|
214
|
+
.with(handle: 'hello2').and_return(app)
|
|
215
|
+
subject.send('apps:rename', 'hello', 'hello2')
|
|
216
|
+
expect(captured_logs).to include(
|
|
217
|
+
'In order for the new app name (hello2) to appear in log drain and '\
|
|
218
|
+
'metric drain destinations, you must restart the app.'
|
|
219
|
+
)
|
|
220
|
+
expect(captured_logs).to include(
|
|
221
|
+
"(git@beta.aptible.com:#{account.handle}/hello2.git)"
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
it 'should fail if app does not exist' do
|
|
225
|
+
expect { subject.send('apps:rename', 'hello2', 'hello3') }
|
|
226
|
+
.to raise_error(/Could not find app hello/)
|
|
227
|
+
end
|
|
228
|
+
it 'should raise error if update fails' do
|
|
229
|
+
response = Faraday::Response.new(status: 422)
|
|
230
|
+
error = HyperResource::ClientError.new('ActiveRecord::RecordInvalid:'\
|
|
231
|
+
' Validation failed: Handle has already been taken, Handle has already'\
|
|
232
|
+
' been taken', response: response)
|
|
233
|
+
expect(app).to receive(:update!)
|
|
234
|
+
.with(handle: 'hello2').and_raise(error)
|
|
235
|
+
expect { subject.send('apps:rename', 'hello', 'hello2') }
|
|
236
|
+
.to raise_error(HyperResource::ClientError)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
200
241
|
describe '#apps:scale' do
|
|
201
242
|
before do
|
|
202
243
|
allow(Aptible::Api::App).to receive(:all) { [app] }
|
|
@@ -22,6 +22,9 @@ describe Aptible::CLI::Agent do
|
|
|
22
22
|
before do
|
|
23
23
|
allow(Aptible::Api::Account).to receive(:all).and_return([account])
|
|
24
24
|
end
|
|
25
|
+
before do
|
|
26
|
+
subject.stub(:validate_image_type) { true }
|
|
27
|
+
end
|
|
25
28
|
|
|
26
29
|
def expect_provision_database(create_opts, provision_opts = {})
|
|
27
30
|
db = Fabricate(:database)
|
|
@@ -876,4 +879,39 @@ describe Aptible::CLI::Agent do
|
|
|
876
879
|
expect(captured_output_text).to eq(expected)
|
|
877
880
|
end
|
|
878
881
|
end
|
|
882
|
+
|
|
883
|
+
describe '#db:rename' do
|
|
884
|
+
before do
|
|
885
|
+
allow(subject).to receive(:options)
|
|
886
|
+
.and_return(environment: account.handle)
|
|
887
|
+
allow(Aptible::Api::Account).to receive(:all) { [account] }
|
|
888
|
+
end
|
|
889
|
+
context 'with environment and db' do
|
|
890
|
+
it 'should rename properly' do
|
|
891
|
+
expect(database).to receive(:update!)
|
|
892
|
+
.with(handle: 'foo2').and_return(database)
|
|
893
|
+
subject.send('db:rename', database.handle, 'foo2')
|
|
894
|
+
expect(captured_logs).to include(
|
|
895
|
+
'In order for the new database name (foo2) to appear in log drain '\
|
|
896
|
+
'and metric drain destinations, you must reload the database.'
|
|
897
|
+
)
|
|
898
|
+
end
|
|
899
|
+
it 'should fail if db does not exist' do
|
|
900
|
+
expect { subject.send('db:rename', 'foo2', 'foo3') }
|
|
901
|
+
.to raise_error(/Could not find database foo2/)
|
|
902
|
+
end
|
|
903
|
+
it 'should raise error if update fails' do
|
|
904
|
+
response = Faraday::Response.new(status: 500)
|
|
905
|
+
error = HyperResource::ClientError.new(
|
|
906
|
+
'An error occurred: Validation failed: Handle has '\
|
|
907
|
+
'already been taken, Handle has already been taken',
|
|
908
|
+
response: response
|
|
909
|
+
)
|
|
910
|
+
expect(database).to receive(:update!)
|
|
911
|
+
.with(handle: 'foo2').and_raise(error)
|
|
912
|
+
expect { subject.send('db:rename', database.handle, 'foo2') }
|
|
913
|
+
.to raise_error(HyperResource::ClientError)
|
|
914
|
+
end
|
|
915
|
+
end
|
|
916
|
+
end
|
|
879
917
|
end
|
|
@@ -10,47 +10,82 @@ describe Aptible::CLI::Agent do
|
|
|
10
10
|
|
|
11
11
|
let(:token) { double 'token' }
|
|
12
12
|
|
|
13
|
-
before do
|
|
13
|
+
before(:each) do
|
|
14
14
|
allow(subject).to receive(:fetch_token) { token }
|
|
15
15
|
allow(Aptible::Api::Account).to receive(:all).with(token: token)
|
|
16
16
|
.and_return([a1, a2])
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
describe('#environment:list') do
|
|
20
|
+
it 'lists avaliable environments' do
|
|
21
|
+
subject.send('environment:list')
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
expect(captured_output_text.split("\n")).to include('foo')
|
|
24
|
+
expect(captured_output_text.split("\n")).to include('bar')
|
|
25
|
+
end
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
|
|
28
|
+
describe('#environment:ca_cert') do
|
|
29
|
+
it 'fetches certs for all avaliable environments' do
|
|
30
|
+
subject.send('environment:ca_cert')
|
|
31
|
+
|
|
32
|
+
expect(captured_output_text.split("\n")).to include('account 1 cert')
|
|
33
|
+
expect(captured_output_text.split("\n")).to include('--account 2 cert--')
|
|
34
|
+
|
|
35
|
+
expected_accounts = [
|
|
36
|
+
{
|
|
37
|
+
'handle' => 'foo',
|
|
38
|
+
'ca_body' => 'account 1 cert',
|
|
39
|
+
'created_at' => fmt_time(a1.created_at)
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
'handle' => 'bar',
|
|
43
|
+
'ca_body' => '--account 2 cert--',
|
|
44
|
+
'created_at' => fmt_time(a2.created_at)
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
expect(captured_output_json.map! { |account| account.except('id') })
|
|
48
|
+
.to eq(expected_accounts)
|
|
49
|
+
end
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
it 'fetches certs for specified environment' do
|
|
52
|
+
subject.options = { environment: 'foo' }
|
|
53
|
+
subject.send('environment:ca_cert')
|
|
54
|
+
|
|
55
|
+
expect(captured_output_text.split("\n")).to include('account 1 cert')
|
|
56
|
+
expect(captured_output_text.split("\n"))
|
|
57
|
+
.to_not include('--account 2 cert--')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
61
|
+
describe('#environment:rename') do
|
|
62
|
+
it 'should rename properly' do
|
|
63
|
+
expect(a1).to receive(:update!)
|
|
64
|
+
.with(handle: 'foo-renamed').and_return(a1)
|
|
65
|
+
subject.send('environment:rename', 'foo', 'foo-renamed')
|
|
66
|
+
expect(captured_logs).to include(
|
|
67
|
+
'In order for the new environment handle (foo-renamed)'
|
|
68
|
+
)
|
|
69
|
+
expect(captured_logs).to include(
|
|
70
|
+
'* Your own external scripts (e.g. for CI/CD)'
|
|
71
|
+
)
|
|
72
|
+
expect(captured_logs).to include(
|
|
73
|
+
'* Git remote URLs (ex: git@beta.aptible.com:foo-renamed'
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
it 'should fail if env does not exist' do
|
|
77
|
+
expect { subject.send('environment:rename', 'foo1', 'foo2') }
|
|
78
|
+
.to raise_error(/Could not find environment foo1/)
|
|
79
|
+
end
|
|
80
|
+
it 'should raise error if update fails' do
|
|
81
|
+
response = Faraday::Response.new(status: 422)
|
|
82
|
+
error = HyperResource::ClientError.new('ActiveRecord::RecordInvalid:'\
|
|
83
|
+
' Validation failed: Handle has already been taken, Handle has already'\
|
|
84
|
+
' been taken', response: response)
|
|
85
|
+
expect(a1).to receive(:update!)
|
|
86
|
+
.with(handle: 'bar').and_raise(error)
|
|
87
|
+
expect { subject.send('environment:rename', 'foo', 'bar') }
|
|
88
|
+
.to raise_error(HyperResource::ClientError)
|
|
89
|
+
end
|
|
55
90
|
end
|
|
56
91
|
end
|
|
@@ -60,4 +60,137 @@ describe Aptible::CLI::Agent do
|
|
|
60
60
|
expect { subject.send(:logs) }.to raise_error(/only one of/im)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
|
+
|
|
64
|
+
describe '#logs_from_archive' do
|
|
65
|
+
context 'using string-matches' do
|
|
66
|
+
let(:files) { %w(file_1 file_2) }
|
|
67
|
+
|
|
68
|
+
before do
|
|
69
|
+
subject.options = {
|
|
70
|
+
region: 'some-region',
|
|
71
|
+
bucket: 'some-bucket',
|
|
72
|
+
decryption_keys: 'mykey',
|
|
73
|
+
string_matches: 'foo',
|
|
74
|
+
download_location: './'
|
|
75
|
+
}
|
|
76
|
+
subject.stub(:info_from_path) { { shasum: 'foo' } }
|
|
77
|
+
subject.stub(:encryption_key) { subject.options[:decryption_keys] }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'download all files' do
|
|
81
|
+
expect(subject).to receive(:ensure_aws_creds)
|
|
82
|
+
expect(subject).to receive(:validate_log_search_options)
|
|
83
|
+
.with(subject.options)
|
|
84
|
+
|
|
85
|
+
expect(subject).to receive(:find_s3_files_by_string_match)
|
|
86
|
+
.with(
|
|
87
|
+
subject.options[:region],
|
|
88
|
+
subject.options[:bucket],
|
|
89
|
+
subject.options[:stack],
|
|
90
|
+
subject.options[:string_matches]
|
|
91
|
+
).and_return(files)
|
|
92
|
+
|
|
93
|
+
files.each do |f|
|
|
94
|
+
expect(subject).to receive(:decrypt_and_translate_s3_file)
|
|
95
|
+
.with(
|
|
96
|
+
f,
|
|
97
|
+
subject.options[:decryption_keys],
|
|
98
|
+
subject.options[:region],
|
|
99
|
+
subject.options[:bucket],
|
|
100
|
+
subject.options[:download_location]
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
subject.send('logs_from_archive')
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
context 'using app/database/endpoint id' do
|
|
108
|
+
let(:files) { %w(file_1 file_2) }
|
|
109
|
+
|
|
110
|
+
before do
|
|
111
|
+
subject.options = {
|
|
112
|
+
region: 'some-region',
|
|
113
|
+
bucket: 'some-bucket',
|
|
114
|
+
stack: 'mystack',
|
|
115
|
+
decryption_keys: 'mykey',
|
|
116
|
+
app_id: 123,
|
|
117
|
+
download_location: './'
|
|
118
|
+
}
|
|
119
|
+
subject.stub(:info_from_path) { { shasum: 'foo' } }
|
|
120
|
+
subject.stub(:encryption_key) { subject.options[:decryption_keys] }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'download all files' do
|
|
124
|
+
expect(subject).to receive(:ensure_aws_creds)
|
|
125
|
+
expect(subject).to receive(:validate_log_search_options)
|
|
126
|
+
.with(subject.options)
|
|
127
|
+
|
|
128
|
+
expect(subject).to receive(:find_s3_files_by_attrs)
|
|
129
|
+
.with(
|
|
130
|
+
subject.options[:region],
|
|
131
|
+
subject.options[:bucket],
|
|
132
|
+
subject.options[:stack],
|
|
133
|
+
{ type: 'apps', id: 123 },
|
|
134
|
+
nil
|
|
135
|
+
).and_return(files)
|
|
136
|
+
|
|
137
|
+
files.each do |f|
|
|
138
|
+
expect(subject).to receive(:decrypt_and_translate_s3_file)
|
|
139
|
+
.with(
|
|
140
|
+
f,
|
|
141
|
+
subject.options[:decryption_keys],
|
|
142
|
+
subject.options[:region],
|
|
143
|
+
subject.options[:bucket],
|
|
144
|
+
subject.options[:download_location]
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
subject.send('logs_from_archive')
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context 'using container id' do
|
|
152
|
+
let(:files) { %w(file_1 file_2) }
|
|
153
|
+
|
|
154
|
+
before do
|
|
155
|
+
subject.options = {
|
|
156
|
+
region: 'some-region',
|
|
157
|
+
bucket: 'some-bucket',
|
|
158
|
+
stack: 'mystack',
|
|
159
|
+
decryption_keys: 'mykey',
|
|
160
|
+
container_id:
|
|
161
|
+
'9080b96447f98b31ef9831d5fd98b09e3c5c545269734e2e825644571152457c',
|
|
162
|
+
download_location: './'
|
|
163
|
+
}
|
|
164
|
+
subject.stub(:info_from_path) { { shasum: 'foo' } }
|
|
165
|
+
subject.stub(:encryption_key) { subject.options[:decryption_keys] }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'download all files' do
|
|
169
|
+
expect(subject).to receive(:ensure_aws_creds)
|
|
170
|
+
expect(subject).to receive(:validate_log_search_options)
|
|
171
|
+
.with(subject.options)
|
|
172
|
+
|
|
173
|
+
expect(subject).to receive(:find_s3_files_by_attrs)
|
|
174
|
+
.with(
|
|
175
|
+
subject.options[:region],
|
|
176
|
+
subject.options[:bucket],
|
|
177
|
+
subject.options[:stack],
|
|
178
|
+
{ container_id: subject.options[:container_id] },
|
|
179
|
+
nil
|
|
180
|
+
).and_return(files)
|
|
181
|
+
|
|
182
|
+
files.each do |f|
|
|
183
|
+
expect(subject).to receive(:decrypt_and_translate_s3_file)
|
|
184
|
+
.with(
|
|
185
|
+
f,
|
|
186
|
+
subject.options[:decryption_keys],
|
|
187
|
+
subject.options[:region],
|
|
188
|
+
subject.options[:bucket],
|
|
189
|
+
subject.options[:download_location]
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
subject.send('logs_from_archive')
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
63
196
|
end
|
|
@@ -145,7 +145,7 @@ describe Aptible::CLI::Agent do
|
|
|
145
145
|
drain_type: :datadog,
|
|
146
146
|
drain_configuration: {
|
|
147
147
|
api_key: 'foobar',
|
|
148
|
-
series_url: 'https://app.datadoghq.eu'
|
|
148
|
+
series_url: 'https://app.datadoghq.eu/api/v1/series'
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
expect_provision_metric_drain(opts)
|
|
@@ -3,6 +3,8 @@ require 'spec_helper'
|
|
|
3
3
|
describe Aptible::CLI::Agent do
|
|
4
4
|
let(:token) { 'some-token' }
|
|
5
5
|
let(:operation) { Fabricate(:operation) }
|
|
6
|
+
let(:net_http_double) { double('Net::HTTP') }
|
|
7
|
+
let(:net_http_get_double) { double('Net::HTTP::Get') }
|
|
6
8
|
|
|
7
9
|
before do
|
|
8
10
|
allow(subject).to receive(:fetch_token).and_return(token)
|
|
@@ -26,4 +28,174 @@ describe Aptible::CLI::Agent do
|
|
|
26
28
|
subject.send('operation:cancel', 1)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
31
|
+
|
|
32
|
+
describe '#operation:follow' do
|
|
33
|
+
it 'fails if the operation cannot be found' do
|
|
34
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
35
|
+
.and_return(nil)
|
|
36
|
+
|
|
37
|
+
expect { subject.send('operation:follow', 1) }
|
|
38
|
+
.to raise_error('Operation #1 not found')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'connects to a running operation' do
|
|
42
|
+
op = Fabricate(:operation, status: 'running', type: 'restart')
|
|
43
|
+
expect(Aptible::Api::Operation).to receive(:find)
|
|
44
|
+
.with(op.id.to_s, token: token).and_return(op)
|
|
45
|
+
|
|
46
|
+
expect(subject).to receive(:attach_to_operation_logs).with(op)
|
|
47
|
+
subject.send('operation:follow', op.id.to_s)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'connects to a queued operation' do
|
|
51
|
+
op = Fabricate(:operation, status: 'queued', type: 'restart')
|
|
52
|
+
expect(Aptible::Api::Operation).to receive(:find)
|
|
53
|
+
.with(op.id.to_s, token: token).and_return(op)
|
|
54
|
+
|
|
55
|
+
expect(subject).to receive(:attach_to_operation_logs).with(op)
|
|
56
|
+
subject.send('operation:follow', op.id.to_s)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'does not connect to a failed operation' do
|
|
60
|
+
id = 34
|
|
61
|
+
status = 'failed'
|
|
62
|
+
op = Fabricate(:operation, id: id, status: status)
|
|
63
|
+
expect(Aptible::Api::Operation).to receive(:find)
|
|
64
|
+
.with(op.id.to_s, token: token).and_return(op)
|
|
65
|
+
|
|
66
|
+
expect { subject.send('operation:follow', op.id.to_s) }
|
|
67
|
+
.to raise_error(Thor::Error, /aptible operation:logs #{id}/)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'does not connect to a succeeded operation' do
|
|
71
|
+
id = 43
|
|
72
|
+
status = 'succeeded'
|
|
73
|
+
op = Fabricate(:operation, id: id, status: status)
|
|
74
|
+
expect(Aptible::Api::Operation).to receive(:find)
|
|
75
|
+
.with(op.id.to_s, token: token).and_return(op)
|
|
76
|
+
|
|
77
|
+
expect { subject.send('operation:follow', op.id.to_s) }
|
|
78
|
+
.to raise_error(Thor::Error, /aptible operation:logs #{id}/)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '#operation:logs' do
|
|
83
|
+
it 'sends operation logs request when subcommand sent' do
|
|
84
|
+
operation_id = SecureRandom.uuid
|
|
85
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
86
|
+
.and_return(Fabricate(
|
|
87
|
+
:operation, status: 'succeeded', id: operation_id
|
|
88
|
+
))
|
|
89
|
+
|
|
90
|
+
# stub out operations call
|
|
91
|
+
response = instance_double(Net::HTTPResponse, body: 'https://s3.aptible.com/not-real/s3')
|
|
92
|
+
|
|
93
|
+
# stub out s3 call
|
|
94
|
+
s3_response = instance_double(Net::HTTPResponse, body: 'Mock logs')
|
|
95
|
+
|
|
96
|
+
allow(Net::HTTP).to receive(:new).twice do |_, _, _|
|
|
97
|
+
net_http_double
|
|
98
|
+
end
|
|
99
|
+
expect(response).to receive(:code).and_return('200')
|
|
100
|
+
expect(s3_response).to receive(:code).and_return('200')
|
|
101
|
+
expect(net_http_double).to receive(:use_ssl=).twice
|
|
102
|
+
expect(net_http_double).to receive(:request).twice do |request|
|
|
103
|
+
if request.path == "/operations/#{operation_id}/logs"
|
|
104
|
+
response
|
|
105
|
+
elsif request.path == '/not-real/s3'
|
|
106
|
+
s3_response
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
subject.send('operation:logs', 1)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'errors when operation is not found' do
|
|
114
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
115
|
+
.and_return(nil)
|
|
116
|
+
|
|
117
|
+
expect { subject.send('operation:logs', 1) }
|
|
118
|
+
.to raise_error('Operation #1 not found')
|
|
119
|
+
end
|
|
120
|
+
it 'errors when operation is not status expected' do
|
|
121
|
+
operation_id = SecureRandom.uuid
|
|
122
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
123
|
+
.and_return(Fabricate(:operation, status: 'queued', id: operation_id))
|
|
124
|
+
|
|
125
|
+
expect { subject.send('operation:logs', 1) }
|
|
126
|
+
.to raise_error('Error - You can view the logs when operation '\
|
|
127
|
+
'is complete.')
|
|
128
|
+
end
|
|
129
|
+
it 'errors when operation logs are not found' do
|
|
130
|
+
operation_id = SecureRandom.uuid
|
|
131
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
132
|
+
.and_return(
|
|
133
|
+
Fabricate(:operation, status: 'succeeded', id: operation_id)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# stub out operations call
|
|
137
|
+
response = Net::HTTPSuccess.new(1.0, '404', 'Not Found')
|
|
138
|
+
expect_any_instance_of(Net::HTTP)
|
|
139
|
+
.to receive(:request)
|
|
140
|
+
.with(an_instance_of(Net::HTTP::Get))
|
|
141
|
+
.and_return(response)
|
|
142
|
+
|
|
143
|
+
expect { subject.send('operation:logs', 1) }
|
|
144
|
+
.to raise_error('Unable to retrieve the operation\'s logs. '\
|
|
145
|
+
'If the issue persists please contact support for assistance.')
|
|
146
|
+
end
|
|
147
|
+
it 'errors when body is empty' do
|
|
148
|
+
operation_id = SecureRandom.uuid
|
|
149
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
150
|
+
.and_return(Fabricate(
|
|
151
|
+
:operation, status: 'succeeded', id: operation_id
|
|
152
|
+
))
|
|
153
|
+
|
|
154
|
+
# stub out operations call
|
|
155
|
+
response = instance_double(Net::HTTPResponse, body: nil)
|
|
156
|
+
|
|
157
|
+
allow(Net::HTTP).to receive(:new) do |_, _, _|
|
|
158
|
+
net_http_double
|
|
159
|
+
end
|
|
160
|
+
expect(response).to receive(:code).and_return('200')
|
|
161
|
+
expect(net_http_double).to receive(:use_ssl=)
|
|
162
|
+
expect(net_http_double).to receive(:request).and_return(response)
|
|
163
|
+
|
|
164
|
+
expect { subject.send('operation:logs', 1) }
|
|
165
|
+
.to raise_error('Unable to retrieve the operation\'s logs. '\
|
|
166
|
+
'If the issue persists please contact support for assistance.')
|
|
167
|
+
end
|
|
168
|
+
it 'errors when s3 itself returns an error code' do
|
|
169
|
+
operation_id = SecureRandom.uuid
|
|
170
|
+
expect(Aptible::Api::Operation).to receive(:find).with(1, token: token)
|
|
171
|
+
.and_return(Fabricate(
|
|
172
|
+
:operation, status: 'succeeded', id: operation_id
|
|
173
|
+
))
|
|
174
|
+
|
|
175
|
+
# stub out operations call
|
|
176
|
+
response = instance_double(Net::HTTPResponse, body: 'https://s3.aptible.com/not-real/s3')
|
|
177
|
+
|
|
178
|
+
# stub out s3 call (to fail)
|
|
179
|
+
expect(response).to receive(:code).and_return('200')
|
|
180
|
+
s3_response = Net::HTTPSuccess.new(1.0, '404', 'Not Found')
|
|
181
|
+
|
|
182
|
+
allow(Net::HTTP).to receive(:new).twice do |_, _, _|
|
|
183
|
+
net_http_double
|
|
184
|
+
end
|
|
185
|
+
expect(net_http_double).to receive(:use_ssl=).twice
|
|
186
|
+
expect(net_http_double).to receive(:request).twice do |request|
|
|
187
|
+
if request.path == "/operations/#{operation_id}/logs"
|
|
188
|
+
response
|
|
189
|
+
elsif request.path == '/not-real/s3'
|
|
190
|
+
s3_response
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
expect { subject.send('operation:logs', 1) }
|
|
195
|
+
.to raise_error('Unable to retrieve operation logs, '\
|
|
196
|
+
'S3 returned response code 404. '\
|
|
197
|
+
'If the issue persists please contact support for '\
|
|
198
|
+
'assistance.')
|
|
199
|
+
end
|
|
200
|
+
end
|
|
29
201
|
end
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
class StubOperation < OpenStruct; end
|
|
2
2
|
|
|
3
|
+
def mock_logs_url(id)
|
|
4
|
+
"https://api.aptible.com/operations/#{id}/logs"
|
|
5
|
+
end
|
|
6
|
+
|
|
3
7
|
Fabricator(:operation, from: :stub_operation) do
|
|
4
8
|
status 'queued'
|
|
5
|
-
resource { Fabricate(:app) }
|
|
6
9
|
errors { Aptible::Resource::Errors.new }
|
|
10
|
+
resource { Fabricate(:app) }
|
|
11
|
+
after_save { |operation| operation.logs_url = mock_logs_url(operation.id) }
|
|
7
12
|
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.19.
|
|
4
|
+
version: 0.19.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Frank Macreery
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aptible-resource
|
|
@@ -136,6 +136,20 @@ dependencies:
|
|
|
136
136
|
- - ">="
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
138
|
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: aws-sdk
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - "~>"
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '2.0'
|
|
146
|
+
type: :runtime
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - "~>"
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '2.0'
|
|
139
153
|
- !ruby/object:Gem::Dependency
|
|
140
154
|
name: activesupport
|
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -294,6 +308,7 @@ files:
|
|
|
294
308
|
- lib/aptible/cli/helpers/log_drain.rb
|
|
295
309
|
- lib/aptible/cli/helpers/metric_drain.rb
|
|
296
310
|
- lib/aptible/cli/helpers/operation.rb
|
|
311
|
+
- lib/aptible/cli/helpers/s3_log_helpers.rb
|
|
297
312
|
- lib/aptible/cli/helpers/security_key.rb
|
|
298
313
|
- lib/aptible/cli/helpers/ssh.rb
|
|
299
314
|
- lib/aptible/cli/helpers/system.rb
|
|
@@ -326,10 +341,12 @@ files:
|
|
|
326
341
|
- script/sync-readme-usage
|
|
327
342
|
- spec/aptible/cli/agent_spec.rb
|
|
328
343
|
- spec/aptible/cli/formatter_spec.rb
|
|
344
|
+
- spec/aptible/cli/helpers/database_spec.rb
|
|
329
345
|
- spec/aptible/cli/helpers/git_remote_handle_strategy_spec.rb
|
|
330
346
|
- spec/aptible/cli/helpers/handle_from_git_remote_spec.rb
|
|
331
347
|
- spec/aptible/cli/helpers/operation_spec.rb
|
|
332
348
|
- spec/aptible/cli/helpers/options_handle_strategy_spec.rb
|
|
349
|
+
- spec/aptible/cli/helpers/s3_log_helpers_spec.rb
|
|
333
350
|
- spec/aptible/cli/helpers/ssh_spec.rb
|
|
334
351
|
- spec/aptible/cli/helpers/token_spec.rb
|
|
335
352
|
- spec/aptible/cli/helpers/tunnel_spec.rb
|
|
@@ -382,7 +399,7 @@ homepage: https://github.com/aptible/aptible-cli
|
|
|
382
399
|
licenses:
|
|
383
400
|
- MIT
|
|
384
401
|
metadata: {}
|
|
385
|
-
post_install_message:
|
|
402
|
+
post_install_message:
|
|
386
403
|
rdoc_options: []
|
|
387
404
|
require_paths:
|
|
388
405
|
- lib
|
|
@@ -397,17 +414,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
397
414
|
- !ruby/object:Gem::Version
|
|
398
415
|
version: '0'
|
|
399
416
|
requirements: []
|
|
400
|
-
rubygems_version: 3.0.3
|
|
401
|
-
signing_key:
|
|
417
|
+
rubygems_version: 3.0.3
|
|
418
|
+
signing_key:
|
|
402
419
|
specification_version: 4
|
|
403
420
|
summary: Command-line interface for Aptible services
|
|
404
421
|
test_files:
|
|
405
422
|
- spec/aptible/cli/agent_spec.rb
|
|
406
423
|
- spec/aptible/cli/formatter_spec.rb
|
|
424
|
+
- spec/aptible/cli/helpers/database_spec.rb
|
|
407
425
|
- spec/aptible/cli/helpers/git_remote_handle_strategy_spec.rb
|
|
408
426
|
- spec/aptible/cli/helpers/handle_from_git_remote_spec.rb
|
|
409
427
|
- spec/aptible/cli/helpers/operation_spec.rb
|
|
410
428
|
- spec/aptible/cli/helpers/options_handle_strategy_spec.rb
|
|
429
|
+
- spec/aptible/cli/helpers/s3_log_helpers_spec.rb
|
|
411
430
|
- spec/aptible/cli/helpers/ssh_spec.rb
|
|
412
431
|
- spec/aptible/cli/helpers/token_spec.rb
|
|
413
432
|
- spec/aptible/cli/helpers/tunnel_spec.rb
|