aptible-cli 0.18.1 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.18.1'.freeze
3
+ VERSION = '0.19.1'.freeze
4
4
  end
5
5
  end
@@ -253,42 +253,6 @@ describe Aptible::CLI::Agent do
253
253
  .to raise_error(/provide at least/im)
254
254
  end
255
255
 
256
- it 'should scale container count (legacy)' do
257
- stub_options
258
- expect(service).to receive(:create_operation!)
259
- .with(type: 'scale', container_count: 3)
260
- .and_return(op)
261
- subject.send('apps:scale', 'web', '3')
262
- expect(captured_logs).to match(/deprecated/i)
263
- end
264
-
265
- it 'should scale container size (legacy)' do
266
- stub_options(size: 90210)
267
- expect(service).to receive(:create_operation!)
268
- .with(type: 'scale', container_size: 90210)
269
- .and_return(op)
270
- subject.send('apps:scale', 'web')
271
- expect(captured_logs).to match(/deprecated/i)
272
- end
273
-
274
- it 'should fail when using both current and legacy count' do
275
- stub_options(container_count: 2)
276
- expect { subject.send('apps:scale', 'web', '3') }
277
- .to raise_error(/count was passed via both/im)
278
- end
279
-
280
- it 'should fail when using both current and legacy size' do
281
- stub_options(container_size: 1024, size: 512)
282
- expect { subject.send('apps:scale', 'web') }
283
- .to raise_error(/size was passed via both/im)
284
- end
285
-
286
- it 'should fail when using too many arguments' do
287
- stub_options
288
- expect { subject.send('apps:scale', 'web', '3', '4') }
289
- .to raise_error(/usage:.*apps:scale/im)
290
- end
291
-
292
256
  it 'should fail if the service does not exist' do
293
257
  stub_options(container_count: 2)
294
258
 
@@ -323,17 +287,6 @@ describe Aptible::CLI::Agent do
323
287
  subject.send('apps:scale', 'web')
324
288
  end.to raise_error(Thor::Error)
325
289
  end
326
-
327
- it 'should fail if number is not a valid number (legacy)' do
328
- allow(subject).to receive(:options) { { app: 'hello' } }
329
- allow(service).to receive(:create_operation) { op }
330
-
331
- expect do
332
- subject.send('apps:scale', 'web', 'potato')
333
- end.to raise_error(ArgumentError)
334
-
335
- expect(captured_logs).to match(/deprecated/i)
336
- end
337
290
  end
338
291
 
339
292
  describe '#apps:deprovision' do
@@ -112,7 +112,7 @@ describe Aptible::CLI::Agent do
112
112
  Fabricate(:database, account: account, handle: default_handle)
113
113
  end
114
114
 
115
- subject.options = { size: s }
115
+ subject.options = { disk_size: s }
116
116
  subject.send('backup:restore', 1)
117
117
  end
118
118
 
@@ -57,16 +57,6 @@ describe Aptible::CLI::Agent do
57
57
  subject.send('db:create', 'foo')
58
58
  end
59
59
 
60
- it 'creates a new DB with a (implicitly) disk size' do
61
- expect_provision_database(
62
- { handle: 'foo', type: 'postgresql', initial_disk_size: 200 },
63
- { disk_size: 200 }
64
- )
65
-
66
- subject.options = { type: 'postgresql', size: 200 }
67
- subject.send('db:create', 'foo')
68
- end
69
-
70
60
  it 'creates a new DB with a disk-size' do
71
61
  expect_provision_database(
72
62
  { handle: 'foo', type: 'postgresql', initial_disk_size: 200 },
@@ -414,18 +404,6 @@ describe Aptible::CLI::Agent do
414
404
  expect(captured_logs).to match(/restarting foobar/i)
415
405
  end
416
406
 
417
- it 'allows restarting a database with (implicitly disk) size' do
418
- expect(database).to receive(:create_operation!)
419
- .with(type: 'restart', disk_size: 40).and_return(op)
420
-
421
- expect(subject).to receive(:attach_to_operation_logs).with(op)
422
-
423
- subject.options = { size: 40 }
424
- subject.send('db:restart', handle)
425
-
426
- expect(captured_logs).to match(/restarting foobar/i)
427
- end
428
-
429
407
  it 'allows restarting a database with a disk-size' do
430
408
  expect(database).to receive(:create_operation!)
431
409
  .with(type: 'restart', disk_size: 40).and_return(op)
@@ -581,10 +559,6 @@ describe Aptible::CLI::Agent do
581
559
  expect_replicate_database(container_size: 40)
582
560
  end
583
561
 
584
- it 'allows replicating a database with an (implicitly) disk size option' do
585
- expect_replicate_database(size: 40)
586
- end
587
-
588
562
  it 'allows replicating a database with a disk-size option' do
589
563
  expect_replicate_database(disk_size: 40)
590
564
  end
@@ -94,6 +94,45 @@ describe Aptible::CLI::Agent do
94
94
  stub_options(ip_whitelist: %w(1.1.1.1))
95
95
  subject.send('endpoints:database:create', 'mydb')
96
96
  end
97
+
98
+ it 'creates an internal Database Endpoint' do
99
+ expect_create_vhost(db.service, internal: true)
100
+ stub_options(internal: true)
101
+ subject.send('endpoints:database:create', 'mydb')
102
+ end
103
+ end
104
+
105
+ describe 'endpoints:database:modify' do
106
+ it 'does not change anything if no options are passed' do
107
+ v = Fabricate(:vhost, service: db.service)
108
+ expect_modify_vhost(v, {})
109
+ stub_options(database: 'mydb')
110
+ subject.send('endpoints:database:modify', v.external_host)
111
+ end
112
+
113
+ it 'adds an IP whitelist' do
114
+ v = Fabricate(:vhost, service: db.service)
115
+ expect_modify_vhost(v, ip_whitelist: %w(1.1.1.1))
116
+
117
+ stub_options(database: 'mydb', ip_whitelist: %w(1.1.1.1))
118
+ subject.send('endpoints:database:modify', v.external_host)
119
+ end
120
+
121
+ it 'removes an IP whitelist' do
122
+ v = Fabricate(:vhost, service: db.service)
123
+ expect_modify_vhost(v, ip_whitelist: [])
124
+
125
+ stub_options(database: 'mydb', :'no-ip_whitelist' => true)
126
+ subject.send('endpoints:database:modify', v.external_host)
127
+ end
128
+
129
+ it 'does not allow disabling and adding an IP whitelist' do
130
+ v = Fabricate(:vhost, service: db.service)
131
+ stub_options(database: 'mydb', ip_whitelist: %w(1.1.1.1),
132
+ :'no-ip_whitelist' => true)
133
+ expect { subject.send('endpoints:database:modify', v.external_host) }
134
+ .to raise_error(/conflicting.*no-ip-whitelist.*ip-whitelist/im)
135
+ end
97
136
  end
98
137
 
99
138
  describe 'endpoints:list' do
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aptible::CLI::Agent do
4
+ let(:account) { Fabricate(:account) }
5
+ let!(:log_drain) do
6
+ Fabricate(:log_drain, handle: 'test', account: account)
7
+ end
8
+
9
+ let(:token) { double('token') }
10
+ before { allow(subject).to receive(:fetch_token).and_return(token) }
11
+
12
+ before do
13
+ allow(Aptible::Api::Account).to receive(:all)
14
+ .with(token: token).and_return([account])
15
+ end
16
+
17
+ describe '#log_drain:list' do
18
+ it 'lists a log drains for an account' do
19
+ subject.send('log_drain:list')
20
+
21
+ out = "=== aptible\n" \
22
+ "test\n"
23
+ expect(captured_output_text).to eq(out)
24
+ end
25
+
26
+ it 'lists log drains across multiple accounts' do
27
+ other_account = Fabricate(:account)
28
+ Fabricate(:log_drain, handle: 'test2', account: other_account)
29
+ accounts = [account, other_account]
30
+ allow(Aptible::Api::Account).to receive(:all).and_return(accounts)
31
+
32
+ subject.send('log_drain:list')
33
+
34
+ out = "=== aptible\n" \
35
+ "test\n" \
36
+ "test2\n"
37
+ expect(captured_output_text).to eq(out)
38
+ end
39
+
40
+ it 'lists log drains for a single account when --environment is included' do
41
+ other_account = Fabricate(:account)
42
+ Fabricate(:log_drain, handle: 'test2', account: other_account)
43
+ accounts = [account, other_account]
44
+ allow(Aptible::Api::Account).to receive(:all).and_return(accounts)
45
+
46
+ subject.options = { environment: account.handle }
47
+ subject.send('log_drain:list')
48
+
49
+ out = "=== aptible\n" \
50
+ "test\n"
51
+ expect(captured_output_text).to eq(out)
52
+ end
53
+ end
54
+
55
+ describe '#log_drain:create' do
56
+ def expect_provision_log_drain(create_opts, provision_opts = {})
57
+ log_drain = Fabricate(:log_drain, account: account)
58
+ op = Fabricate(:operation)
59
+
60
+ expect(account).to receive(:create_log_drain!)
61
+ .with(**create_opts).and_return(log_drain)
62
+
63
+ expect(log_drain).to receive(:create_operation)
64
+ .with(type: :provision, **provision_opts).and_return(op)
65
+
66
+ expect(subject).to receive(:attach_to_operation_logs).with(op)
67
+ end
68
+
69
+ context 'elasticsearch' do
70
+ let(:db) { Fabricate(:database, account: account, id: 5) }
71
+
72
+ it 'creates a new Elasticsearch log drain' do
73
+ opts = {
74
+ handle: 'test',
75
+ drain_apps: nil,
76
+ drain_databases: nil,
77
+ drain_ephemeral_sessions: nil,
78
+ drain_proxies: nil,
79
+ drain_type: :elasticsearch_database,
80
+ logging_token: nil,
81
+ database_id: db.id
82
+ }
83
+ expect_provision_log_drain(opts)
84
+
85
+ subject.options = {
86
+ db: db.handle,
87
+ environment: account.handle
88
+ }
89
+ subject.send('log_drain:create:elasticsearch', 'test')
90
+ end
91
+
92
+ it 'creates a new Elasticsearch log drain with a pipeline' do
93
+ opts = {
94
+ handle: 'test-es',
95
+ drain_apps: nil,
96
+ drain_databases: nil,
97
+ drain_ephemeral_sessions: nil,
98
+ drain_proxies: nil,
99
+ drain_type: :elasticsearch_database,
100
+ logging_token: 'test-pipeline',
101
+ database_id: db.id
102
+ }
103
+ expect_provision_log_drain(opts)
104
+
105
+ subject.options = {
106
+ db: db.handle,
107
+ environment: account.handle,
108
+ pipeline: 'test-pipeline'
109
+ }
110
+ subject.send('log_drain:create:elasticsearch', 'test-es')
111
+ end
112
+ end
113
+
114
+ # HTTPS, Datadog, Sumologic, and LogDNA are all similar enough
115
+ # that they're not tested individually
116
+ context 'https' do
117
+ it 'creates a new HTTPS log drain' do
118
+ opts = {
119
+ handle: 'test-https',
120
+ drain_apps: nil,
121
+ drain_databases: nil,
122
+ drain_ephemeral_sessions: nil,
123
+ drain_proxies: nil,
124
+ drain_type: :https_post,
125
+ url: 'https://test.foo.com'
126
+ }
127
+ expect_provision_log_drain(opts)
128
+
129
+ subject.options = {
130
+ environment: account.handle,
131
+ url: 'https://test.foo.com'
132
+ }
133
+ subject.send('log_drain:create:https', 'test-https')
134
+ end
135
+ end
136
+
137
+ # Syslog and Papertrail are similar enough that they're
138
+ # not tested individually
139
+ context 'syslog' do
140
+ it 'creates a new syslog log drain' do
141
+ opts = {
142
+ handle: 'test-syslog',
143
+ drain_host: 'test.foo.com',
144
+ drain_port: 2468,
145
+ logging_token: nil,
146
+ drain_apps: nil,
147
+ drain_databases: nil,
148
+ drain_ephemeral_sessions: nil,
149
+ drain_proxies: nil,
150
+ drain_type: :syslog_tls_tcp
151
+ }
152
+ expect_provision_log_drain(opts)
153
+
154
+ subject.options = {
155
+ environment: account.handle,
156
+ host: 'test.foo.com',
157
+ port: 2468
158
+ }
159
+ subject.send('log_drain:create:syslog', 'test-syslog')
160
+ end
161
+
162
+ it 'creates a new syslog log drain with a logging token' do
163
+ opts = {
164
+ handle: 'test-syslog',
165
+ drain_host: 'test.foo.com',
166
+ drain_port: 2468,
167
+ logging_token: 'test-token',
168
+ drain_apps: nil,
169
+ drain_databases: nil,
170
+ drain_ephemeral_sessions: nil,
171
+ drain_proxies: nil,
172
+ drain_type: :syslog_tls_tcp
173
+ }
174
+ expect_provision_log_drain(opts)
175
+
176
+ subject.options = {
177
+ environment: account.handle,
178
+ host: 'test.foo.com',
179
+ port: 2468,
180
+ token: 'test-token'
181
+ }
182
+ subject.send('log_drain:create:syslog', 'test-syslog')
183
+ end
184
+ end
185
+ end
186
+
187
+ describe '#log_drain:deprovision' do
188
+ let(:operation) { Fabricate(:operation, resource: log_drain) }
189
+
190
+ it 'deprovisions a log drain' do
191
+ expect(log_drain).to receive(:create_operation)
192
+ .with(type: :deprovision).and_return(operation)
193
+ expect(subject).to receive(:attach_to_operation_logs).with(operation)
194
+ subject.send('log_drain:deprovision', log_drain.handle)
195
+ end
196
+
197
+ it 'does not fail if the operation cannot be found' do
198
+ expect(log_drain).to receive(:create_operation)
199
+ .with(type: :deprovision).and_return(operation)
200
+ response = Faraday::Response.new(status: 404)
201
+ error = HyperResource::ClientError.new('Not Found', response: response)
202
+ expect(subject).to receive(:attach_to_operation_logs).with(operation)
203
+ .and_raise(error)
204
+ subject.send('log_drain:deprovision', log_drain.handle)
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aptible::CLI::Agent do
4
+ let(:account) { Fabricate(:account) }
5
+ let!(:metric_drain) do
6
+ Fabricate(:metric_drain, handle: 'test', account: account)
7
+ end
8
+
9
+ let(:token) { double('token') }
10
+ before { allow(subject).to receive(:fetch_token).and_return(token) }
11
+
12
+ before do
13
+ allow(Aptible::Api::Account).to receive(:all)
14
+ .with(token: token).and_return([account])
15
+ end
16
+
17
+ describe '#metric_drain:list' do
18
+ it 'lists a metric drains for an account' do
19
+ subject.send('metric_drain:list')
20
+
21
+ out = "=== aptible\n" \
22
+ "test\n"
23
+ expect(captured_output_text).to eq(out)
24
+ end
25
+
26
+ it 'lists metric drains across multiple accounts' do
27
+ other_account = Fabricate(:account)
28
+ Fabricate(:metric_drain, handle: 'test2', account: other_account)
29
+ accounts = [account, other_account]
30
+ allow(Aptible::Api::Account).to receive(:all).and_return(accounts)
31
+
32
+ subject.send('metric_drain:list')
33
+
34
+ out = "=== aptible\n" \
35
+ "test\n" \
36
+ "test2\n"
37
+ expect(captured_output_text).to eq(out)
38
+ end
39
+
40
+ it 'lists metric drains for a single account with --environment' do
41
+ other_account = Fabricate(:account)
42
+ Fabricate(:metric_drain, handle: 'test2', account: other_account)
43
+ accounts = [account, other_account]
44
+ allow(Aptible::Api::Account).to receive(:all).and_return(accounts)
45
+
46
+ subject.options = { environment: account.handle }
47
+ subject.send('metric_drain:list')
48
+
49
+ out = "=== aptible\n" \
50
+ "test\n"
51
+ expect(captured_output_text).to eq(out)
52
+ end
53
+ end
54
+
55
+ describe '#metric_drain:create' do
56
+ def expect_provision_metric_drain(create_opts, provision_opts = {})
57
+ metric_drain = Fabricate(:metric_drain, account: account)
58
+ op = Fabricate(:operation)
59
+
60
+ expect(account).to receive(:create_metric_drain!)
61
+ .with(**create_opts).and_return(metric_drain)
62
+
63
+ expect(metric_drain).to receive(:create_operation)
64
+ .with(type: :provision, **provision_opts).and_return(op)
65
+
66
+ expect(subject).to receive(:attach_to_operation_logs).with(op)
67
+ end
68
+
69
+ context 'influxdb' do
70
+ let(:db) { Fabricate(:database, account: account, id: 5) }
71
+
72
+ it 'creates a new InfluxDB metric drain' do
73
+ opts = {
74
+ handle: 'test-influxdb',
75
+ drain_type: :influxdb_database,
76
+ database_id: db.id
77
+ }
78
+ expect_provision_metric_drain(opts)
79
+
80
+ subject.options = {
81
+ db: db.handle,
82
+ environment: account.handle
83
+ }
84
+ subject.send('metric_drain:create:influxdb', 'test-influxdb')
85
+ end
86
+ end
87
+
88
+ context 'influxdb:custom' do
89
+ it 'creates a new InfluxDB metric drain' do
90
+ opts = {
91
+ handle: 'test-influxdb-custom',
92
+ drain_type: :influxdb,
93
+ drain_configuration: {
94
+ address: 'https://test.foo.com:443',
95
+ database: 'foobar',
96
+ password: 'bar',
97
+ username: 'foo'
98
+ }
99
+ }
100
+ expect_provision_metric_drain(opts)
101
+
102
+ subject.options = {
103
+ environment: account.handle,
104
+ username: 'foo',
105
+ password: 'bar',
106
+ db: 'foobar',
107
+ url: 'https://test.foo.com:443'
108
+ }
109
+ subject.send('metric_drain:create:influxdb:custom',
110
+ 'test-influxdb-custom')
111
+ end
112
+ end
113
+
114
+ context 'datadog' do
115
+ it 'creates a new Datadog metric drain' do
116
+ opts = {
117
+ handle: 'test-datadog',
118
+ drain_type: :datadog,
119
+ drain_configuration: {
120
+ api_key: 'foobar'
121
+ }
122
+ }
123
+ expect_provision_metric_drain(opts)
124
+
125
+ subject.options = {
126
+ environment: account.handle,
127
+ api_key: 'foobar'
128
+ }
129
+ subject.send('metric_drain:create:datadog', 'test-datadog')
130
+ end
131
+
132
+ it 'raises an error when the Datadog site is invalid' do
133
+ subject.options = {
134
+ environment: account.handle,
135
+ api_key: 'foobar',
136
+ site: 'BAD'
137
+ }
138
+ expect { subject.send('metric_drain:create:datadog', 'test-datadog') }
139
+ .to raise_error(Thor::Error, /Invalid Datadog site/i)
140
+ end
141
+
142
+ it 'creates a new Datadog metric drain with a Datadog site defined' do
143
+ opts = {
144
+ handle: 'test-datadog',
145
+ drain_type: :datadog,
146
+ drain_configuration: {
147
+ api_key: 'foobar',
148
+ series_url: 'https://app.datadoghq.eu'
149
+ }
150
+ }
151
+ expect_provision_metric_drain(opts)
152
+
153
+ subject.options = {
154
+ environment: account.handle,
155
+ api_key: 'foobar',
156
+ site: 'EU1'
157
+ }
158
+ subject.send('metric_drain:create:datadog', 'test-datadog')
159
+ end
160
+ end
161
+ end
162
+
163
+ describe '#metric_drain:deprovision' do
164
+ let(:operation) { Fabricate(:operation, resource: metric_drain) }
165
+
166
+ it 'deprovisions a log drain' do
167
+ expect(metric_drain).to receive(:create_operation)
168
+ .with(type: :deprovision).and_return(operation)
169
+ expect(subject).to receive(:attach_to_operation_logs).with(operation)
170
+ subject.send('metric_drain:deprovision', metric_drain.handle)
171
+ end
172
+
173
+ it 'does not fail if the operation cannot be found' do
174
+ expect(metric_drain).to receive(:create_operation)
175
+ .with(type: :deprovision).and_return(operation)
176
+ response = Faraday::Response.new(status: 404)
177
+ error = HyperResource::ClientError.new('Not Found', response: response)
178
+ expect(subject).to receive(:attach_to_operation_logs).with(operation)
179
+ .and_raise(error)
180
+ subject.send('metric_drain:deprovision', metric_drain.handle)
181
+ end
182
+ end
183
+ end