aptible-cli 0.19.6 → 0.19.8

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
  SHA256:
3
- metadata.gz: bc1a5d4d43d71792087a31f7994ed5a612dfe93ba90fd2ff3cebe63f5c8888a7
4
- data.tar.gz: 7ac17167081e9edb3104b372a06622d736ce03051bb07279a690716eb7787ac9
3
+ metadata.gz: 5297ec1eb7d106b2b69705d31ab05426fb64d8c553b170918c4090aaf582398f
4
+ data.tar.gz: d0478f4e443b88f9def1a45a9db9b029f62d36e6d73bc53c6a686c858bfd3e83
5
5
  SHA512:
6
- metadata.gz: b65bc6b0af2a8f88cc0fd72b47948ed01bbb3a32201ccaefd2b2dae3cdbe8042c6133e77862d79c63d4a0fa303460a5d453fa9f9a7ea7543af111a8101388fda
7
- data.tar.gz: 7f8e3a443093b34b3b7428f249918e35327f92043cf0a35a248f7ef8aa8842f1a0d7ba8d48eeaa29f7d341be049f8f19e6b86d9d9ef91ea7401babaad2091fbf
6
+ metadata.gz: 9e0490170403f7272b481a09272115aafc5d1a3c65b21c26d357e149fc305b5ffef0c04903d8e677f865b992d76551828e3621a338d2bc6c6ee69a33222b8ddf
7
+ data.tar.gz: fa77c4fd5462b8b99a5f0ae8146e876e163e8faf6b2e34aa1275cdbea6d920d40b48b9f1fb08820b92d3b5ab02de681c17b20a4192305b7c674760cdfbd80ba3
data/.travis.yml CHANGED
@@ -8,7 +8,11 @@ rvm:
8
8
  - "2.4"
9
9
  - "2.5"
10
10
  - "2.6"
11
+ - "2.7"
11
12
 
13
+ before_install:
14
+ - ./cleanup_bundler
15
+ - gem install bundler -v '< 2'
12
16
  script:
13
17
  - bundle exec rake
14
18
  - bundle exec script/sync-readme-usage
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
- # ![](https://raw.github.com/aptible/straptible/master/lib/straptible/rails/templates/public.api/icon-60px.png) Aptible CLI
1
+ <br>
2
+ <img src="https://user-images.githubusercontent.com/4295811/226700092-ffbd0c01-dba1-4880-8b77-a4d26e6228f0.svg" width="64">
2
3
 
3
- [![Gem Version](https://badge.fury.io/rb/aptible-cli.png)](https://rubygems.org/gems/aptible-cli)
4
- [![Build Status](https://travis-ci.org/aptible/aptible-cli.png?branch=master)](https://travis-ci.org/aptible/aptible-cli)
5
- [![Dependency Status](https://gemnasium.com/aptible/aptible-cli.png)](https://gemnasium.com/aptible/aptible-cli)
6
- [![Roadmap](https://badge.waffle.io/aptible/aptible-cli.svg?label=ready&title=roadmap)](http://waffle.io/aptible/aptible-cli)
4
+ # Aptible CLI
7
5
 
8
6
  Command-line interface for Aptible services.
9
7
 
@@ -11,8 +9,8 @@ Command-line interface for Aptible services.
11
9
 
12
10
  **NOTE: To install the `aptible` tool as a system-level binary, Aptible
13
11
  recommends you install the
14
- [Aptible Toolbelt](https://support.aptible.com/toolbelt/)**, which is faster
15
- and more robust.
12
+ [Aptible CLI](https://www.aptible.com/docs/cli)** directly, which is
13
+ faster and more robust.
16
14
 
17
15
  Add the following line to your application's Gemfile.
18
16
 
@@ -85,9 +83,12 @@ Commands:
85
83
  aptible login # Log in to Aptible
86
84
  aptible logs [--app APP | --database DATABASE] # Follows logs from a running app or database
87
85
  aptible logs_from_archive --bucket NAME --region REGION --stack NAME [ --decryption-keys ONE [OR MORE] ] [ --download-location LOCATION ] [ [ --string-matches ONE [OR MORE] ] | [ --app-id ID | --database-id ID | --endpoint-id ID | --container-id ID ] [ --start-date YYYY-MM-DD --end-date YYYY-MM-DD ] ] --bucket=BUCKET --region=REGION --stack=STACK # Retrieves container logs from an S3 archive in your own AWS account. You must provide your AWS credentials via the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
86
+ aptible maintenance:apps # List Apps impacted by maintenance schedules where restarts are required
87
+ aptible maintenance:dbs # List Databases impacted by maintenance schedules where restarts are required
88
88
  aptible metric_drain:create:datadog HANDLE --api_key DATADOG_API_KEY --site DATADOG_SITE --environment ENVIRONMENT # Create a Datadog Metric Drain
89
89
  aptible metric_drain:create:influxdb HANDLE --db DATABASE_HANDLE --environment ENVIRONMENT # Create an InfluxDB Metric Drain
90
90
  aptible metric_drain:create:influxdb:custom HANDLE --username USERNAME --password PASSWORD --url URL_INCLUDING_PORT --db INFLUX_DATABASE_NAME --environment ENVIRONMENT # Create an InfluxDB Metric Drain
91
+ aptible metric_drain:create:influxdb:customv2 HANDLE --org ORGANIZATION --token INFLUX_TOKEN --url URL_INCLUDING_PORT --bucket INFLUX_BUCKET_NAME --environment ENVIRONMENT # Create an InfluxDB v2 Metric Drain
91
92
  aptible metric_drain:deprovision HANDLE --environment ENVIRONMENT # Deprovisions a Metric Drain
92
93
  aptible metric_drain:list # List all Metric Drains
93
94
  aptible operation:cancel OPERATION_ID # Cancel a running operation
data/SECURITY.md ADDED
@@ -0,0 +1,23 @@
1
+ # Aptible Open Source Security Policies and Procedures
2
+
3
+ This document outlines security procedures and general policies for the Aptible open source projects as found on https://github.com/aptible.
4
+
5
+ * [Reporting a Vulnerability](#reporting-a-vulnerability)
6
+ * [Responsible Disclosure Policy](#responsible-disclosure-policy)
7
+
8
+ ## Reporting a Vulnerability
9
+
10
+ The Aptible team and community take all security vulnerabilities
11
+ seriously. Thank you for improving the security of our open source software. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions.
12
+
13
+ Report security vulnerabilities by emailing the Aptible security team at:
14
+
15
+ security@aptible.com
16
+
17
+ Security researchers can also privately report security vulnerabilities to repository maintainers using the GitHub "Report a Vulnerability" feature. [See how-to here](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability).
18
+
19
+ The Aptible team will acknowledge your email within 24 business hours and send a detailed response within 48 business hours indicating the next steps in handling your report. The Aptible security team will keep you informed of the progress and may ask for additional information or guidance.
20
+
21
+ ## Responsible Disclosure Policy
22
+
23
+ Please see Aptible's Responsible Disclosure Policy here: https://www.aptible.com/legal/responsible-disclosure/
data/aptible-cli.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ['lib']
22
22
 
23
23
  spec.add_dependency 'aptible-resource', '~> 1.1'
24
- spec.add_dependency 'aptible-api', '~> 1.2'
24
+ spec.add_dependency 'aptible-api', '~> 1.4'
25
25
  spec.add_dependency 'aptible-auth', '~> 1.2.4'
26
26
  spec.add_dependency 'aptible-billing', '~> 1.0'
27
27
  spec.add_dependency 'thor', '~> 0.20.0'
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'chronic_duration', '~> 0.10.6'
31
31
  spec.add_dependency 'cbor'
32
32
  spec.add_dependency 'aws-sdk', '~> 2.0'
33
+ spec.add_dependency 'bigdecimal', '~> 1.3.5' # https://github.com/ruby/bigdecimal#which-version-should-you-select
33
34
 
34
35
  # Temporarily pin ffi until https://github.com/ffi/ffi/issues/868 is fixed
35
36
  spec.add_dependency 'ffi', '<= 1.14.1' if Gem.win_platform?
data/cleanup_bundler ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Newer rubies have Bundler 2.x installed as default so it can't be deleted
4
+ # We need Bundler 1.x
5
+
6
+ gempaths = `gem env gempath`.split(':')
7
+ gempaths.each do |gempath|
8
+ # lookup bundler-*.gemspec files and delete them
9
+ # this is the only way to completely cleanup default bundler
10
+ # Note: the bundler gemspecs' paths are different for CRuby and JRuby
11
+ Dir.glob(gempath.strip + '/specifications/**/bundler-*.gemspec').each do |p|
12
+ File.delete(p)
13
+ end
14
+ end
@@ -21,7 +21,9 @@ require_relative 'helpers/security_key'
21
21
  require_relative 'helpers/config_path'
22
22
  require_relative 'helpers/log_drain'
23
23
  require_relative 'helpers/metric_drain'
24
+ require_relative 'helpers/date_helpers'
24
25
  require_relative 'helpers/s3_log_helpers'
26
+ require_relative 'helpers/maintenance'
25
27
 
26
28
  require_relative 'subcommands/apps'
27
29
  require_relative 'subcommands/config'
@@ -39,6 +41,7 @@ require_relative 'subcommands/inspect'
39
41
  require_relative 'subcommands/endpoints'
40
42
  require_relative 'subcommands/log_drain'
41
43
  require_relative 'subcommands/metric_drain'
44
+ require_relative 'subcommands/maintenance'
42
45
 
43
46
  module Aptible
44
47
  module CLI
@@ -49,6 +52,7 @@ module Aptible
49
52
  include Helpers::Ssh
50
53
  include Helpers::System
51
54
  include Helpers::ConfigPath
55
+ include Helpers::DateHelpers
52
56
  include Subcommands::Apps
53
57
  include Subcommands::Config
54
58
  include Subcommands::DB
@@ -65,6 +69,7 @@ module Aptible
65
69
  include Subcommands::Endpoints
66
70
  include Subcommands::LogDrain
67
71
  include Subcommands::MetricDrain
72
+ include Subcommands::Maintenance
68
73
 
69
74
  # Forward return codes on failures.
70
75
  def self.exit_on_failure?
@@ -0,0 +1,34 @@
1
+ module Aptible
2
+ module CLI
3
+ module Helpers
4
+ module DateHelpers
5
+ # This should only be used by the method processing user date input in
6
+ # S3LogHelpers. It is used to process a user-provided string into UTC.
7
+ def utc_date(date_string)
8
+ t_fmt = '%Y-%m-%d %Z'
9
+ Time.strptime("#{date_string} UTC", t_fmt)
10
+ rescue ArgumentError
11
+ raise Thor::Error, 'Please provide dates in YYYY-MM-DD format'
12
+ end
13
+
14
+ # This should only be used by the method processing timestamps from S3
15
+ # file names in S3LogHelpers. The file name does not include any time
16
+ # zone information, but we know it to be in UTC, so we add the "Z"
17
+ def utc_datetime(datetime_string)
18
+ Time.parse("#{datetime_string}Z")
19
+ rescue ArgumentError
20
+ nil
21
+ end
22
+
23
+ # This is used to format timestamps returned by our API into a more
24
+ # readable format.
25
+ # EG, "2023-09-05T22:00:00.000Z" returns "2023-09-05 22:00:00 UTC"
26
+ def utc_string(datetime_string)
27
+ Time.parse(datetime_string)
28
+ rescue ArgumentError
29
+ nil
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ require 'aptible/api'
2
+
3
+ module Aptible
4
+ module CLI
5
+ module Helpers
6
+ module Maintenance
7
+ include Helpers::Token
8
+
9
+ def maintenance_apps
10
+ Aptible::Api::MaintenanceApp.all(token: fetch_token)
11
+ end
12
+
13
+ def maintenance_databases
14
+ Aptible::Api::MaintenanceDatabase.all(token: fetch_token)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -5,6 +5,8 @@ module Aptible
5
5
  module CLI
6
6
  module Helpers
7
7
  module S3LogHelpers
8
+ include Helpers::DateHelpers
9
+
8
10
  def ensure_aws_creds
9
11
  cred_errors = []
10
12
  unless ENV['AWS_ACCESS_KEY_ID']
@@ -190,19 +192,6 @@ module Aptible
190
192
  true
191
193
  end
192
194
 
193
- def utc_date(date_string)
194
- t_fmt = '%Y-%m-%d %Z'
195
- Time.strptime("#{date_string} UTC", t_fmt)
196
- rescue ArgumentError
197
- raise Thor::Error, 'Please provide dates in YYYY-MM-DD format'
198
- end
199
-
200
- def utc_datetime(datetime_string)
201
- Time.parse("#{datetime_string}Z")
202
- rescue ArgumentError
203
- nil
204
- end
205
-
206
195
  def encryption_key(filesum, possible_keys)
207
196
  # The key can be determined from the sum
208
197
  possible_keys.each do |k|
@@ -2,6 +2,8 @@ module Aptible
2
2
  module CLI
3
3
  module ResourceFormatter
4
4
  class << self
5
+ include Helpers::DateHelpers
6
+
5
7
  NO_NESTING = Object.new.freeze
6
8
 
7
9
  def inject_backup(node, backup, include_db: false)
@@ -50,8 +52,7 @@ module Aptible
50
52
  end
51
53
  end
52
54
 
53
- if bu_operation && \
54
- backup.manual && !backup.copied_from
55
+ if bu_operation && !backup.copied_from
55
56
  node.keyed_object('created_from_operation', 'id') do |n|
56
57
  inject_operation(n, bu_operation)
57
58
  end
@@ -236,6 +237,28 @@ module Aptible
236
237
  attach_account(node, account)
237
238
  end
238
239
 
240
+ def inject_maintenance(
241
+ node,
242
+ command_prefix,
243
+ maintenance_resource,
244
+ account
245
+ )
246
+ node.value('id', maintenance_resource.id)
247
+ raw_start, raw_end = maintenance_resource.maintenance_deadline
248
+ window_start = utc_string(raw_start)
249
+ window_end = utc_string(raw_end)
250
+ label = "#{maintenance_resource.handle} between #{window_start} "\
251
+ "and #{window_end}"
252
+ restart_command = "#{command_prefix}"\
253
+ " #{maintenance_resource.handle}"\
254
+ " --environment #{account.handle}"
255
+ node.value('label', label)
256
+ node.value('handle', maintenance_resource.handle)
257
+ node.value('restart_command', restart_command)
258
+
259
+ attach_account(node, account)
260
+ end
261
+
239
262
  private
240
263
 
241
264
  def attach_account(node, account)
@@ -41,7 +41,7 @@ module Aptible
41
41
  git_ref = options[:git_commitish]
42
42
  if options[:git_detach]
43
43
  if git_ref
44
- raise Thor::Error, 'The options --git-committish and ' \
44
+ raise Thor::Error, 'The options --git-commitish and ' \
45
45
  '--git-detach are incompatible'
46
46
  end
47
47
  git_ref = NULL_SHA1
@@ -11,6 +11,7 @@ module Aptible
11
11
  include Helpers::Operation
12
12
  include Helpers::AppOrDatabase
13
13
  include Helpers::S3LogHelpers
14
+ include Helpers::DateHelpers
14
15
 
15
16
  desc 'logs [--app APP | --database DATABASE]',
16
17
  'Follows logs from a running app or database'
@@ -0,0 +1,97 @@
1
+ module Aptible
2
+ module CLI
3
+ module Subcommands
4
+ module Maintenance
5
+ def self.included(thor)
6
+ thor.class_eval do
7
+ include Helpers::Environment
8
+ include Helpers::Maintenance
9
+ include Helpers::Token
10
+
11
+ desc 'maintenance:apps',
12
+ 'List Apps impacted by maintenance schedules where '\
13
+ 'restarts are required'
14
+ option :environment
15
+ define_method 'maintenance:apps' do
16
+ found_maintenance = false
17
+ m = maintenance_apps
18
+ Formatter.render(Renderer.current) do |root|
19
+ root.grouped_keyed_list(
20
+ { 'environment' => 'handle' },
21
+ 'label'
22
+ ) do |node|
23
+ scoped_environments(options).each do |account|
24
+ m.select { |app| app.account.id == account.id }
25
+ .each do |app|
26
+ next unless app.maintenance_deadline
27
+ found_maintenance = true
28
+ node.object do |n|
29
+ ResourceFormatter.inject_maintenance(
30
+ n,
31
+ 'aptible restart --app',
32
+ app,
33
+ account
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ if found_maintenance
41
+ explanation 'app'
42
+ else
43
+ no_maintenances 'app'
44
+ end
45
+ end
46
+ desc 'maintenance:dbs',
47
+ 'List Databases impacted by maintenance schedules where '\
48
+ 'restarts are required'
49
+ option :environment
50
+ define_method 'maintenance:dbs' do
51
+ found_maintenance = false
52
+ m = maintenance_databases
53
+ Formatter.render(Renderer.current) do |root|
54
+ root.grouped_keyed_list(
55
+ { 'environment' => 'handle' },
56
+ 'label'
57
+ ) do |node|
58
+ scoped_environments(options).each do |account|
59
+ m.select { |db| db.account.id == account.id }
60
+ .each do |db|
61
+ next unless db.maintenance_deadline
62
+ found_maintenance = true
63
+ node.object do |n|
64
+ ResourceFormatter.inject_maintenance(
65
+ n,
66
+ 'aptible db:restart',
67
+ db,
68
+ account
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ if found_maintenance
76
+ explanation 'database'
77
+ else
78
+ no_maintenances 'database'
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def explanation(resource_type)
85
+ CLI.logger.warn "\nYou may restart these #{resource_type}(s)"\
86
+ ' at any time, or Aptible will restart it'\
87
+ ' during the defined window.'
88
+ end
89
+
90
+ def no_maintenances(resource_type)
91
+ CLI.logger.info "\nNo #{resource_type}s found affected "\
92
+ 'by maintenance schedules.'
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -5,6 +5,7 @@ module Aptible
5
5
  SITES = {
6
6
  'US1' => 'https://app.datadoghq.com',
7
7
  'US3' => 'https://us3.datadoghq.com',
8
+ 'US5' => 'https://us5.datadoghq.com',
8
9
  'EU1' => 'https://app.datadoghq.eu',
9
10
  'US1-FED' => 'https://app.ddog-gov.com'
10
11
  }.freeze
@@ -84,6 +85,35 @@ module Aptible
84
85
  create_metric_drain(account, opts)
85
86
  end
86
87
 
88
+ desc 'metric_drain:create:influxdb:customv2 HANDLE '\
89
+ '--org ORGANIZATION --token INFLUX_TOKEN ' \
90
+ '--url URL_INCLUDING_PORT ' \
91
+ '--bucket INFLUX_BUCKET_NAME ' \
92
+ '--environment ENVIRONMENT',
93
+ 'Create an InfluxDB v2 Metric Drain'
94
+ option :bucket, type: :string
95
+ option :org, type: :string
96
+ option :token, type: :string
97
+ option :url, type: :string
98
+ option :environment
99
+ define_method 'metric_drain:create:influxdb:customv2' do |handle|
100
+ account = ensure_environment(options)
101
+
102
+ config = {
103
+ address: options[:url],
104
+ org: options[:org],
105
+ authToken: options[:token],
106
+ bucket: options[:bucket]
107
+ }
108
+ opts = {
109
+ handle: handle,
110
+ drain_configuration: config,
111
+ drain_type: :influxdb2
112
+ }
113
+
114
+ create_metric_drain(account, opts)
115
+ end
116
+
87
117
  desc 'metric_drain:create:datadog HANDLE '\
88
118
  '--api_key DATADOG_API_KEY '\
89
119
  '--site DATADOG_SITE ' \
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.19.6'.freeze
3
+ VERSION = '0.19.8'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aptible::CLI::Helpers::DateHelpers do
4
+ subject { Class.new.send(:include, described_class).new }
5
+
6
+ describe '#utc_string' do
7
+ it 'should accept a Datetime string from our API and return a UTC string' do
8
+ result = subject.utc_string('2023-09-05T22:00:00.000Z')
9
+ expect(result).to eq '2023-09-05 22:00:00 UTC'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aptible::CLI::Agent do
4
+ include Aptible::CLI::Helpers::DateHelpers
5
+
6
+ let(:token) { double('token') }
7
+
8
+ before do
9
+ allow(subject).to receive(:ask)
10
+ allow(subject).to receive(:save_token)
11
+ allow(subject).to receive(:fetch_token) { token }
12
+ end
13
+
14
+ let(:handle) { 'foobar' }
15
+ let(:stack) { Fabricate(:stack, internal_domain: 'aptible.in') }
16
+ let(:account) { Fabricate(:account, stack: stack) }
17
+ let(:database) { Fabricate(:database, handle: handle, account: account) }
18
+ let(:staging) { Fabricate(:account, handle: 'staging') }
19
+ let(:prod) { Fabricate(:account, handle: 'production') }
20
+ let(:window_start) { '2073-09-05T22:00:00.000Z' }
21
+ let(:window_end) { '2073-09-05T23:00:00.000Z' }
22
+ let(:maintenance_dbs) do
23
+ [
24
+ [staging, 'staging-redis-db', [window_start, window_end]],
25
+ [staging, 'staging-postgres-db', nil],
26
+ [prod, 'prod-elsearch-db', [window_start, window_end]],
27
+ [prod, 'prod-postgres-db', nil]
28
+ ].map do |a, h, m|
29
+ Fabricate(
30
+ :maintenance_database,
31
+ account: a,
32
+ handle: h,
33
+ maintenance_deadline: m
34
+ )
35
+ end
36
+ end
37
+ let(:maintenance_apps) do
38
+ [
39
+ [staging, 'staging-app-1', [window_start, window_end]],
40
+ [staging, 'staging-app-2', nil],
41
+ [prod, 'prod-app-1', [window_start, window_end]],
42
+ [prod, 'prod-app-2', nil]
43
+ ].map do |a, h, m|
44
+ Fabricate(
45
+ :maintenance_app,
46
+ account: a,
47
+ handle: h,
48
+ maintenance_deadline: m
49
+ )
50
+ end
51
+ end
52
+
53
+ describe '#maintenance:dbs' do
54
+ before do
55
+ token = 'the-token'
56
+ allow(subject).to receive(:fetch_token) { token }
57
+ allow(Aptible::Api::Account).to receive(:all)
58
+ .with(token: token)
59
+ .and_return([staging, prod])
60
+ allow(Aptible::Api::MaintenanceDatabase).to receive(:all)
61
+ .with(token: token)
62
+ .and_return(maintenance_dbs)
63
+ end
64
+
65
+ context 'when no account is specified' do
66
+ it 'prints out the grouped database handles for all accounts' do
67
+ subject.send('maintenance:dbs')
68
+
69
+ expect(captured_output_text).to include('=== staging')
70
+ expect(captured_output_text).to include('staging-redis-db')
71
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
72
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
73
+ expect(captured_output_text).not_to include('staging-postgres-db')
74
+
75
+ expect(captured_output_text).to include('=== production')
76
+ expect(captured_output_text).to include('prod-elsearch-db')
77
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
78
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
79
+ expect(captured_output_text).not_to include('prod-postgres-db')
80
+
81
+ expect(captured_output_json.to_s)
82
+ .to include(
83
+ 'aptible db:restart staging-redis-db --environment staging'
84
+ )
85
+ expect(captured_output_json.to_s)
86
+ .to include(
87
+ 'aptible db:restart prod-elsearch-db --environment production'
88
+ )
89
+ end
90
+ end
91
+
92
+ context 'when a valid account is specified' do
93
+ it 'prints out the database handles for the account' do
94
+ subject.options = { environment: 'staging' }
95
+ subject.send('maintenance:dbs')
96
+
97
+ expect(captured_output_text).to include('=== staging')
98
+ expect(captured_output_text).to include('staging-redis-db')
99
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
100
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
101
+ expect(captured_output_text).not_to include('staging-postgres-db')
102
+
103
+ expect(captured_output_text).not_to include('=== production')
104
+ expect(captured_output_text).not_to include('prod-elsearch-db')
105
+ expect(captured_output_text).not_to include('prod-postgres-db')
106
+
107
+ expect(captured_output_json.to_s)
108
+ .to include(
109
+ 'aptible db:restart staging-redis-db --environment staging'
110
+ )
111
+ end
112
+ end
113
+
114
+ context 'when an invalid account is specified' do
115
+ it 'prints out an error' do
116
+ subject.options = { environment: 'foo' }
117
+ expect { subject.send('maintenance:dbs') }
118
+ .to raise_error('Specified account does not exist')
119
+ end
120
+ end
121
+ end
122
+ describe '#maintenance:apps' do
123
+ before do
124
+ token = 'the-token'
125
+ allow(subject).to receive(:fetch_token) { token }
126
+ allow(Aptible::Api::Account).to receive(:all).with(token: token)
127
+ .and_return([staging, prod])
128
+ allow(Aptible::Api::MaintenanceApp).to receive(:all).with(token: token)
129
+ .and_return(maintenance_apps)
130
+ end
131
+
132
+ context 'when no account is specified' do
133
+ it 'prints out the grouped app handles for all accounts' do
134
+ subject.send('maintenance:apps')
135
+
136
+ expect(captured_output_text).to include('=== staging')
137
+ expect(captured_output_text).to include('staging-app-1')
138
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
139
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
140
+ expect(captured_output_text).not_to include('staging-app-2')
141
+
142
+ expect(captured_output_text).to include('=== production')
143
+ expect(captured_output_text).to include('prod-app-1')
144
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
145
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
146
+ expect(captured_output_text).not_to include('prod-app-2')
147
+
148
+ expect(captured_output_json.to_s)
149
+ .to include(
150
+ 'aptible restart --app staging-app-1 --environment staging'
151
+ )
152
+ expect(captured_output_json.to_s)
153
+ .to include(
154
+ 'aptible restart --app prod-app-1 --environment production'
155
+ )
156
+ end
157
+ end
158
+
159
+ context 'when a valid account is specified' do
160
+ it 'prints out the app handles for the account' do
161
+ subject.options = { environment: 'staging' }
162
+ subject.send('maintenance:apps')
163
+
164
+ expect(captured_output_text).to include('=== staging')
165
+ expect(captured_output_text).to include('staging-app-1')
166
+ expect(captured_output_text).to include('2073-09-05 22:00:00 UTC')
167
+ expect(captured_output_text).to include('2073-09-05 23:00:00 UTC')
168
+ expect(captured_output_text).not_to include('staging-app-2')
169
+
170
+ expect(captured_output_text).not_to include('=== production')
171
+ expect(captured_output_text).not_to include('prod-app-1')
172
+ expect(captured_output_text).not_to include('prod-app-2')
173
+
174
+ expect(captured_output_json.to_s)
175
+ .to include(
176
+ 'aptible restart --app staging-app-1 --environment staging'
177
+ )
178
+ end
179
+ end
180
+
181
+ context 'when an invalid account is specified' do
182
+ it 'prints out an error' do
183
+ subject.options = { environment: 'foo' }
184
+ expect { subject.send('maintenance:apps') }
185
+ .to raise_error('Specified account does not exist')
186
+ end
187
+ end
188
+ end
189
+ end
@@ -86,7 +86,7 @@ describe Aptible::CLI::Agent do
86
86
  end
87
87
 
88
88
  context 'influxdb:custom' do
89
- it 'creates a new InfluxDB metric drain' do
89
+ it 'creates a new InfluxDB v1 metric drain' do
90
90
  opts = {
91
91
  handle: 'test-influxdb-custom',
92
92
  drain_type: :influxdb,
@@ -111,6 +111,32 @@ describe Aptible::CLI::Agent do
111
111
  end
112
112
  end
113
113
 
114
+ context 'influxdb:customv2' do
115
+ it 'creates a new InfluxDB v2 metric drain' do
116
+ opts = {
117
+ handle: 'test-influxdb2-custom',
118
+ drain_type: :influxdb2,
119
+ drain_configuration: {
120
+ address: 'https://test.foo.com:443',
121
+ org: 'foobar',
122
+ authToken: 'bar',
123
+ bucket: 'foo'
124
+ }
125
+ }
126
+ expect_provision_metric_drain(opts)
127
+
128
+ subject.options = {
129
+ environment: account.handle,
130
+ bucket: 'foo',
131
+ token: 'bar',
132
+ org: 'foobar',
133
+ url: 'https://test.foo.com:443'
134
+ }
135
+ subject.send('metric_drain:create:influxdb:customv2',
136
+ 'test-influxdb2-custom')
137
+ end
138
+ end
139
+
114
140
  context 'datadog' do
115
141
  it 'creates a new Datadog metric drain' do
116
142
  opts = {
@@ -0,0 +1,10 @@
1
+ class StubMaintenanceApp < OpenStruct; end
2
+
3
+ Fabricator(:maintenance_app, from: :stub_maintenance_app) do
4
+ id { Fabricate.sequence(:app_id) { |i| i } }
5
+ handle 'hello'
6
+ status 'provisioned'
7
+ account
8
+ created_at { Time.now }
9
+ maintenance_deadline { [Time.now + 1.minute, Time.now + 2.minute] }
10
+ end
@@ -0,0 +1,10 @@
1
+ class StubMaintenanceDatabase < OpenStruct; end
2
+
3
+ Fabricator(:maintenance_database, from: :stub_maintenance_database) do
4
+ id { Fabricate.sequence(:database_id) { |i| i } }
5
+ handle 'hello'
6
+ status 'provisioned'
7
+ account
8
+ created_at { Time.now }
9
+ maintenance_deadline { [Time.now + 1.minute, Time.now + 2.minute] }
10
+ 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.6
4
+ version: 0.19.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-28 00:00:00.000000000 Z
11
+ date: 2024-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-resource
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.2'
33
+ version: '1.4'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.2'
40
+ version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: aptible-auth
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '2.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: bigdecimal
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.3.5
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 1.3.5
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: activesupport
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -284,9 +298,11 @@ files:
284
298
  - LICENSE.md
285
299
  - README.md
286
300
  - Rakefile
301
+ - SECURITY.md
287
302
  - appveyor.yml
288
303
  - aptible-cli.gemspec
289
304
  - bin/aptible
305
+ - cleanup_bundler
290
306
  - codecov.yml
291
307
  - lib/aptible/cli.rb
292
308
  - lib/aptible/cli/agent.rb
@@ -304,8 +320,10 @@ files:
304
320
  - lib/aptible/cli/helpers/app_or_database.rb
305
321
  - lib/aptible/cli/helpers/config_path.rb
306
322
  - lib/aptible/cli/helpers/database.rb
323
+ - lib/aptible/cli/helpers/date_helpers.rb
307
324
  - lib/aptible/cli/helpers/environment.rb
308
325
  - lib/aptible/cli/helpers/log_drain.rb
326
+ - lib/aptible/cli/helpers/maintenance.rb
309
327
  - lib/aptible/cli/helpers/metric_drain.rb
310
328
  - lib/aptible/cli/helpers/operation.rb
311
329
  - lib/aptible/cli/helpers/s3_log_helpers.rb
@@ -331,6 +349,7 @@ files:
331
349
  - lib/aptible/cli/subcommands/inspect.rb
332
350
  - lib/aptible/cli/subcommands/log_drain.rb
333
351
  - lib/aptible/cli/subcommands/logs.rb
352
+ - lib/aptible/cli/subcommands/maintenance.rb
334
353
  - lib/aptible/cli/subcommands/metric_drain.rb
335
354
  - lib/aptible/cli/subcommands/operation.rb
336
355
  - lib/aptible/cli/subcommands/rebuild.rb
@@ -342,6 +361,7 @@ files:
342
361
  - spec/aptible/cli/agent_spec.rb
343
362
  - spec/aptible/cli/formatter_spec.rb
344
363
  - spec/aptible/cli/helpers/database_spec.rb
364
+ - spec/aptible/cli/helpers/date_helpers_spec.rb
345
365
  - spec/aptible/cli/helpers/git_remote_handle_strategy_spec.rb
346
366
  - spec/aptible/cli/helpers/handle_from_git_remote_spec.rb
347
367
  - spec/aptible/cli/helpers/operation_spec.rb
@@ -363,6 +383,7 @@ files:
363
383
  - spec/aptible/cli/subcommands/inspect_spec.rb
364
384
  - spec/aptible/cli/subcommands/log_drain_spec.rb
365
385
  - spec/aptible/cli/subcommands/logs_spec.rb
386
+ - spec/aptible/cli/subcommands/maintenance_spec.rb
366
387
  - spec/aptible/cli/subcommands/metric_drain_spec.rb
367
388
  - spec/aptible/cli/subcommands/operation_spec.rb
368
389
  - spec/aptible/cli/subcommands/rebuild_spec.rb
@@ -380,6 +401,8 @@ files:
380
401
  - spec/fabricators/database_fabricator.rb
381
402
  - spec/fabricators/database_image_fabricator.rb
382
403
  - spec/fabricators/log_drain_fabricator.rb
404
+ - spec/fabricators/maintenance_app_fabricator.rb
405
+ - spec/fabricators/maintenance_database_fabricator.rb
383
406
  - spec/fabricators/metric_drain_fabricator.rb
384
407
  - spec/fabricators/operation_fabricator.rb
385
408
  - spec/fabricators/service_fabricator.rb
@@ -414,7 +437,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
414
437
  - !ruby/object:Gem::Version
415
438
  version: '0'
416
439
  requirements: []
417
- rubygems_version: 3.0.3
440
+ rubygems_version: 3.1.6
418
441
  signing_key:
419
442
  specification_version: 4
420
443
  summary: Command-line interface for Aptible services
@@ -422,6 +445,7 @@ test_files:
422
445
  - spec/aptible/cli/agent_spec.rb
423
446
  - spec/aptible/cli/formatter_spec.rb
424
447
  - spec/aptible/cli/helpers/database_spec.rb
448
+ - spec/aptible/cli/helpers/date_helpers_spec.rb
425
449
  - spec/aptible/cli/helpers/git_remote_handle_strategy_spec.rb
426
450
  - spec/aptible/cli/helpers/handle_from_git_remote_spec.rb
427
451
  - spec/aptible/cli/helpers/operation_spec.rb
@@ -443,6 +467,7 @@ test_files:
443
467
  - spec/aptible/cli/subcommands/inspect_spec.rb
444
468
  - spec/aptible/cli/subcommands/log_drain_spec.rb
445
469
  - spec/aptible/cli/subcommands/logs_spec.rb
470
+ - spec/aptible/cli/subcommands/maintenance_spec.rb
446
471
  - spec/aptible/cli/subcommands/metric_drain_spec.rb
447
472
  - spec/aptible/cli/subcommands/operation_spec.rb
448
473
  - spec/aptible/cli/subcommands/rebuild_spec.rb
@@ -460,6 +485,8 @@ test_files:
460
485
  - spec/fabricators/database_fabricator.rb
461
486
  - spec/fabricators/database_image_fabricator.rb
462
487
  - spec/fabricators/log_drain_fabricator.rb
488
+ - spec/fabricators/maintenance_app_fabricator.rb
489
+ - spec/fabricators/maintenance_database_fabricator.rb
463
490
  - spec/fabricators/metric_drain_fabricator.rb
464
491
  - spec/fabricators/operation_fabricator.rb
465
492
  - spec/fabricators/service_fabricator.rb