aptible-cli 0.16.4 → 0.16.9
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 +1 -0
- data/Gemfile +0 -1
- data/README.md +48 -48
- data/appveyor.yml +1 -7
- data/aptible-cli.gemspec +2 -2
- data/bin/aptible +1 -1
- data/lib/aptible/cli/agent.rb +14 -0
- data/lib/aptible/cli/helpers/database.rb +14 -4
- data/lib/aptible/cli/resource_formatter.rb +36 -1
- data/lib/aptible/cli/subcommands/backup.rb +43 -13
- data/lib/aptible/cli/subcommands/db.rb +28 -4
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/subcommands/backup_spec.rb +49 -2
- data/spec/aptible/cli/subcommands/db_spec.rb +66 -1
- data/spec/spec_helper.rb +0 -8
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c9170855dbd5cf2000e91c545d019cc2e3b2ed5115f1a0c5baa7ae9961d1479
|
|
4
|
+
data.tar.gz: '07528b128af3da4c9b21e8bc17e07ed3184f0e58cb367804435bc48600dbc6dd'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c145b617a27c9eb6fbda60b4953e12252c4d64b3ecc6d32493ad3f87f5a146567424f812cb495064974bdda7ece202bdbfbadc338a1f2252ace2e93451231f5
|
|
7
|
+
data.tar.gz: 5c200a84cdcfaebb37b45959d6d90177aec02218f3f53bff08f7d60127975c1eed5355b60340f91474bef3fb2df5de9bcf82c0ca930b6fb8d18775c225475fb1
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
[](https://rubygems.org/gems/aptible-cli)
|
|
4
4
|
[](https://travis-ci.org/aptible/aptible-cli)
|
|
5
5
|
[](https://gemnasium.com/aptible/aptible-cli)
|
|
6
|
-
[](https://codecov.io/gh/aptible/aptible-cli)
|
|
7
6
|
[](http://waffle.io/aptible/aptible-cli)
|
|
8
7
|
|
|
9
8
|
Command-line interface for Aptible services.
|
|
@@ -29,53 +28,54 @@ From `aptible help`:
|
|
|
29
28
|
<!-- BEGIN USAGE -->
|
|
30
29
|
```
|
|
31
30
|
Commands:
|
|
32
|
-
aptible apps
|
|
33
|
-
aptible apps:create HANDLE
|
|
34
|
-
aptible apps:deprovision
|
|
35
|
-
aptible apps:scale SERVICE [--container-count COUNT] [--container-size SIZE_MB]
|
|
36
|
-
aptible backup:list DB_HANDLE
|
|
37
|
-
aptible backup:
|
|
38
|
-
aptible backup:
|
|
39
|
-
aptible
|
|
40
|
-
aptible config
|
|
41
|
-
aptible config:
|
|
42
|
-
aptible config:
|
|
43
|
-
aptible config:
|
|
44
|
-
aptible
|
|
45
|
-
aptible db:
|
|
46
|
-
aptible db:
|
|
47
|
-
aptible db:
|
|
48
|
-
aptible db:
|
|
49
|
-
aptible db:
|
|
50
|
-
aptible db:
|
|
51
|
-
aptible db:
|
|
52
|
-
aptible db:
|
|
53
|
-
aptible db:
|
|
54
|
-
aptible db:
|
|
55
|
-
aptible db:
|
|
56
|
-
aptible db:
|
|
57
|
-
aptible
|
|
58
|
-
aptible
|
|
59
|
-
aptible
|
|
60
|
-
aptible endpoints:
|
|
61
|
-
aptible endpoints:
|
|
62
|
-
aptible endpoints:https:
|
|
63
|
-
aptible endpoints:
|
|
64
|
-
aptible endpoints:
|
|
65
|
-
aptible endpoints:
|
|
66
|
-
aptible endpoints:tcp:
|
|
67
|
-
aptible endpoints:
|
|
68
|
-
aptible endpoints:tls:
|
|
69
|
-
aptible
|
|
70
|
-
aptible
|
|
71
|
-
aptible
|
|
72
|
-
aptible
|
|
73
|
-
aptible
|
|
74
|
-
aptible
|
|
75
|
-
aptible
|
|
76
|
-
aptible
|
|
77
|
-
aptible
|
|
78
|
-
aptible
|
|
31
|
+
aptible apps # List all applications
|
|
32
|
+
aptible apps:create HANDLE # Create a new application
|
|
33
|
+
aptible apps:deprovision # Deprovision an app
|
|
34
|
+
aptible apps:scale SERVICE [--container-count COUNT] [--container-size SIZE_MB] # Scale a service
|
|
35
|
+
aptible backup:list DB_HANDLE # List backups for a database
|
|
36
|
+
aptible backup:orphaned # List backups associated with deprovisioned databases
|
|
37
|
+
aptible backup:purge BACKUP_ID # Permanently delete a backup and any copies of it
|
|
38
|
+
aptible backup:restore BACKUP_ID [--environment ENVIRONMENT_HANDLE] [--handle HANDLE] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Restore a backup
|
|
39
|
+
aptible config # Print an app's current configuration
|
|
40
|
+
aptible config:add [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
|
|
41
|
+
aptible config:rm [VAR1] [VAR2] [...] # Remove an ENV variable from an app
|
|
42
|
+
aptible config:set [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
|
|
43
|
+
aptible config:unset [VAR1] [VAR2] [...] # Remove an ENV variable from an app
|
|
44
|
+
aptible db:backup HANDLE # Backup a database
|
|
45
|
+
aptible db:clone SOURCE DEST # Clone a database to create a new one
|
|
46
|
+
aptible db:create HANDLE [--type TYPE] [--version VERSION] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Create a new database
|
|
47
|
+
aptible db:deprovision HANDLE # Deprovision a database
|
|
48
|
+
aptible db:dump HANDLE [pg_dump options] # Dump a remote database to file
|
|
49
|
+
aptible db:execute HANDLE SQL_FILE [--on-error-stop] # Executes sql against a database
|
|
50
|
+
aptible db:list # List all databases
|
|
51
|
+
aptible db:reload HANDLE # Reload a database
|
|
52
|
+
aptible db:replicate HANDLE REPLICA_HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] [--logical --version VERSION] [--key-arn KEY_ARN] # Create a replica/follower of a database
|
|
53
|
+
aptible db:restart HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] # Restart a database
|
|
54
|
+
aptible db:tunnel HANDLE # Create a local tunnel to a database
|
|
55
|
+
aptible db:url HANDLE # Display a database URL
|
|
56
|
+
aptible db:versions # List available database versions
|
|
57
|
+
aptible deploy [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...] # Deploy an app
|
|
58
|
+
aptible domains # Print an app's current virtual domains - DEPRECATED
|
|
59
|
+
aptible endpoints:database:create DATABASE # Create a Database Endpoint
|
|
60
|
+
aptible endpoints:deprovision [--app APP | --database DATABASE] ENDPOINT_HOSTNAME # Deprovision an App or Database Endpoint
|
|
61
|
+
aptible endpoints:https:create [--app APP] SERVICE # Create an App HTTPS Endpoint
|
|
62
|
+
aptible endpoints:https:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App HTTPS Endpoint
|
|
63
|
+
aptible endpoints:list [--app APP | --database DATABASE] # List Endpoints for an App or Database
|
|
64
|
+
aptible endpoints:renew [--app APP] ENDPOINT_HOSTNAME # Renew an App Managed TLS Endpoint
|
|
65
|
+
aptible endpoints:tcp:create [--app APP] SERVICE # Create an App TCP Endpoint
|
|
66
|
+
aptible endpoints:tcp:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TCP Endpoint
|
|
67
|
+
aptible endpoints:tls:create [--app APP] SERVICE # Create an App TLS Endpoint
|
|
68
|
+
aptible endpoints:tls:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TLS Endpoint
|
|
69
|
+
aptible help [COMMAND] # Describe available commands or one specific command
|
|
70
|
+
aptible login # Log in to Aptible
|
|
71
|
+
aptible logs [--app APP | --database DATABASE] # Follows logs from a running app or database
|
|
72
|
+
aptible operation:cancel OPERATION_ID # Cancel a running operation
|
|
73
|
+
aptible ps # Display running processes for an app - DEPRECATED
|
|
74
|
+
aptible rebuild # Rebuild an app, and restart its services
|
|
75
|
+
aptible restart # Restart all services associated with an app
|
|
76
|
+
aptible services # List Services for an App
|
|
77
|
+
aptible ssh [COMMAND] # Run a command against an app
|
|
78
|
+
aptible version # Print Aptible CLI version
|
|
79
79
|
```
|
|
80
80
|
<!-- END USAGE -->
|
|
81
81
|
|
data/appveyor.yml
CHANGED
|
@@ -8,12 +8,6 @@ environment:
|
|
|
8
8
|
- RUBY_VERSION: 23
|
|
9
9
|
|
|
10
10
|
install:
|
|
11
|
-
# The SSL_CERT_* environment variables are here since otherwise calls to
|
|
12
|
-
# codecov.io wtill not work. These variables do have to be set in order for
|
|
13
|
-
# the gem to make calls to the Aptible API, since otherwise Ruby will fail
|
|
14
|
-
# with a certificate verification error.
|
|
15
|
-
- set SSL_CERT_DIR=%PROGRAMFILES%\Git\mingw64\ssl\certs
|
|
16
|
-
- set SSL_CERT_FILE=%PROGRAMFILES%\Git\mingw64\ssl\cert.pem
|
|
17
11
|
# Override PATHEXT so our ssh bat file has a higher precedence.
|
|
18
12
|
- set PATHEXT=.BAT;.COM;.EXE;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
|
|
19
13
|
- set PATH=C:\Ruby%RUBY_VERSION%-x64\bin;%PATH%
|
|
@@ -28,4 +22,4 @@ before_test:
|
|
|
28
22
|
- bundle -v
|
|
29
23
|
|
|
30
24
|
test_script:
|
|
31
|
-
- bundle exec rake ci
|
|
25
|
+
- bundle exec rake ci
|
data/aptible-cli.gemspec
CHANGED
|
@@ -21,8 +21,8 @@ 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.
|
|
25
|
-
spec.add_dependency 'aptible-auth', '~> 1.
|
|
24
|
+
spec.add_dependency 'aptible-api', '~> 1.2'
|
|
25
|
+
spec.add_dependency 'aptible-auth', '~> 1.2.3'
|
|
26
26
|
spec.add_dependency 'aptible-billing', '~> 1.0'
|
|
27
27
|
spec.add_dependency 'thor', '~> 0.20.0'
|
|
28
28
|
spec.add_dependency 'git'
|
data/bin/aptible
CHANGED
|
@@ -10,7 +10,7 @@ end
|
|
|
10
10
|
begin
|
|
11
11
|
Aptible::CLI::Agent.start
|
|
12
12
|
rescue HyperResource::ClientError => e
|
|
13
|
-
m = if e.body['error']
|
|
13
|
+
m = if %w(invalid_token expired_token).include? e.body['error']
|
|
14
14
|
'API authentication error: please run aptible login'
|
|
15
15
|
else
|
|
16
16
|
"An error occurred: #{e.body['message']}"
|
data/lib/aptible/cli/agent.rb
CHANGED
|
@@ -67,6 +67,7 @@ module Aptible
|
|
|
67
67
|
def initialize(*)
|
|
68
68
|
nag_toolbelt unless toolbelt?
|
|
69
69
|
Aptible::Resource.configure { |conf| conf.user_agent = version_string }
|
|
70
|
+
warn_sso_enforcement
|
|
70
71
|
super
|
|
71
72
|
end
|
|
72
73
|
|
|
@@ -232,6 +233,19 @@ module Aptible
|
|
|
232
233
|
end
|
|
233
234
|
end
|
|
234
235
|
|
|
236
|
+
def warn_sso_enforcement
|
|
237
|
+
# If the user is also a member of
|
|
238
|
+
token = fetch_token
|
|
239
|
+
reauth = Aptible::Auth::ReauthenticateOrganization.all(token: token)
|
|
240
|
+
return if reauth.empty?
|
|
241
|
+
|
|
242
|
+
CLI.logger.warn(['WARNING: You will need to use the appropriate',
|
|
243
|
+
'login method (SSO or Aptible credentials) to access',
|
|
244
|
+
'these organizations:',
|
|
245
|
+
reauth.map(&:name)].join(' '))
|
|
246
|
+
rescue StandardError
|
|
247
|
+
end
|
|
248
|
+
|
|
235
249
|
def version_string
|
|
236
250
|
bits = [
|
|
237
251
|
'aptible-cli',
|
|
@@ -49,11 +49,20 @@ module Aptible
|
|
|
49
49
|
|
|
50
50
|
def replicate_database(source, dest_handle, options)
|
|
51
51
|
replication_params = {
|
|
52
|
-
type: 'replicate',
|
|
53
52
|
handle: dest_handle,
|
|
54
53
|
container_size: options[:container_size],
|
|
55
|
-
disk_size: options[:size]
|
|
54
|
+
disk_size: options[:size],
|
|
55
|
+
key_arn: options[:key_arn]
|
|
56
56
|
}.reject { |_, v| v.nil? }
|
|
57
|
+
|
|
58
|
+
if options[:logical]
|
|
59
|
+
replication_params[:type] = 'replicate_logical'
|
|
60
|
+
replication_params[:docker_ref] =
|
|
61
|
+
options[:database_image].docker_repo
|
|
62
|
+
else
|
|
63
|
+
replication_params[:type] = 'replicate'
|
|
64
|
+
end
|
|
65
|
+
|
|
57
66
|
op = source.create_operation!(replication_params)
|
|
58
67
|
attach_to_operation_logs(op)
|
|
59
68
|
|
|
@@ -96,10 +105,11 @@ module Aptible
|
|
|
96
105
|
|
|
97
106
|
def local_url(credential, local_port)
|
|
98
107
|
remote_url = credential.connection_url
|
|
99
|
-
uri = URI.parse(remote_url)
|
|
100
108
|
|
|
109
|
+
uri = URI.parse(remote_url)
|
|
110
|
+
domain = credential.database.account.stack.internal_domain
|
|
101
111
|
"#{uri.scheme}://#{uri.user}:#{uri.password}@" \
|
|
102
|
-
"localhost
|
|
112
|
+
"localhost.#{domain}:#{local_port}#{uri.path}"
|
|
103
113
|
end
|
|
104
114
|
|
|
105
115
|
def find_credential(database, type = nil)
|
|
@@ -4,6 +4,41 @@ module Aptible
|
|
|
4
4
|
class << self
|
|
5
5
|
NO_NESTING = Object.new.freeze
|
|
6
6
|
|
|
7
|
+
def inject_backup(node, backup, include_db: false)
|
|
8
|
+
description = "#{backup.id}: #{backup.created_at}, " \
|
|
9
|
+
"#{backup.aws_region}"
|
|
10
|
+
|
|
11
|
+
if include_db
|
|
12
|
+
db = backup.database_with_deleted
|
|
13
|
+
node.keyed_object('database', 'id') do |n|
|
|
14
|
+
inject_deleted_database(n, db, backup.account)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
description = "#{description}, " \
|
|
18
|
+
"#{db.handle} deleted at #{db.deleted_at}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
node.value('id', backup.id)
|
|
22
|
+
node.value('description', description)
|
|
23
|
+
node.value('created_at', backup.created_at)
|
|
24
|
+
node.value('region', backup.aws_region)
|
|
25
|
+
node.value('size', backup.size)
|
|
26
|
+
|
|
27
|
+
if backup.copied_from
|
|
28
|
+
node.keyed_object('copied_from', 'description') do |n|
|
|
29
|
+
inject_backup(n, backup.copied_from)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def inject_deleted_database(node, database, account)
|
|
35
|
+
node.value('id', database.id)
|
|
36
|
+
node.value('handle', database.handle)
|
|
37
|
+
node.value('type', database.type)
|
|
38
|
+
node.value('deleted_at', database.deleted_at)
|
|
39
|
+
attach_account(node, account)
|
|
40
|
+
end
|
|
41
|
+
|
|
7
42
|
def inject_account(node, account)
|
|
8
43
|
node.value('id', account.id)
|
|
9
44
|
node.value('handle', account.handle)
|
|
@@ -47,6 +82,7 @@ module Aptible
|
|
|
47
82
|
|
|
48
83
|
node.value('type', database.type)
|
|
49
84
|
node.value('status', database.status)
|
|
85
|
+
|
|
50
86
|
node.value('connection_url', database.connection_url)
|
|
51
87
|
|
|
52
88
|
node.list('credentials') do |creds_list|
|
|
@@ -54,7 +90,6 @@ module Aptible
|
|
|
54
90
|
creds_list.object { |n| inject_credential(n, cred) }
|
|
55
91
|
end
|
|
56
92
|
end
|
|
57
|
-
|
|
58
93
|
attach_account(node, account)
|
|
59
94
|
end
|
|
60
95
|
|
|
@@ -9,13 +9,15 @@ module Aptible
|
|
|
9
9
|
|
|
10
10
|
desc 'backup:restore BACKUP_ID ' \
|
|
11
11
|
'[--environment ENVIRONMENT_HANDLE] [--handle HANDLE] ' \
|
|
12
|
-
'[--container-size SIZE_MB] [--disk-size SIZE_GB]'
|
|
12
|
+
'[--container-size SIZE_MB] [--disk-size SIZE_GB] ' \
|
|
13
|
+
'[--key-arn KEY_ARN]',
|
|
13
14
|
'Restore a backup'
|
|
14
15
|
option :handle, desc: 'a name to use for the new database'
|
|
15
16
|
option :environment, desc: 'a different environment to restore to'
|
|
16
17
|
option :container_size, type: :numeric
|
|
17
18
|
option :size, type: :numeric
|
|
18
19
|
option :disk_size, type: :numeric
|
|
20
|
+
option :key_arn, type: :string
|
|
19
21
|
define_method 'backup:restore' do |backup_id|
|
|
20
22
|
backup = Aptible::Api::Backup.find(backup_id, token: fetch_token)
|
|
21
23
|
raise Thor::Error, "Backup ##{backup_id} not found" if backup.nil?
|
|
@@ -23,7 +25,8 @@ module Aptible
|
|
|
23
25
|
handle = options[:handle]
|
|
24
26
|
unless handle
|
|
25
27
|
ts_suffix = backup.created_at.getgm.strftime '%Y-%m-%d-%H-%M-%S'
|
|
26
|
-
handle =
|
|
28
|
+
handle =
|
|
29
|
+
"#{backup.database_with_deleted.handle}-at-#{ts_suffix}"
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
destination_account = if options[:environment]
|
|
@@ -37,7 +40,8 @@ module Aptible
|
|
|
37
40
|
handle: handle,
|
|
38
41
|
container_size: options[:container_size],
|
|
39
42
|
disk_size: options[:disk_size] || options[:size],
|
|
40
|
-
destination_account: destination_account
|
|
43
|
+
destination_account: destination_account,
|
|
44
|
+
key_arn: options[:key_arn]
|
|
41
45
|
}.delete_if { |_, v| v.nil? }
|
|
42
46
|
|
|
43
47
|
CLI.logger.warn([
|
|
@@ -69,24 +73,50 @@ module Aptible
|
|
|
69
73
|
database = ensure_database(options.merge(db: handle))
|
|
70
74
|
|
|
71
75
|
Formatter.render(Renderer.current) do |root|
|
|
72
|
-
root.keyed_list('description') do |
|
|
76
|
+
root.keyed_list('description') do |node|
|
|
73
77
|
database.each_backup do |backup|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
if backup.created_at < min_created_at && !backup.copied_from
|
|
79
|
+
break
|
|
80
|
+
end
|
|
81
|
+
node.object do |n|
|
|
82
|
+
ResourceFormatter.inject_backup(n, backup)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
desc 'backup:orphaned', 'List backups associated with ' \
|
|
90
|
+
'deprovisioned databases'
|
|
91
|
+
option :environment
|
|
92
|
+
option :max_age, default: '1y',
|
|
93
|
+
desc: 'Limit backups returned '\
|
|
94
|
+
'(example usage: 1w, 1y, etc.)'
|
|
95
|
+
define_method 'backup:orphaned' do
|
|
96
|
+
age = ChronicDuration.parse(options[:max_age])
|
|
97
|
+
raise Thor::Error, "Invalid age: #{options[:max_age]}" if age.nil?
|
|
98
|
+
min_created_at = Time.now - age
|
|
99
|
+
|
|
100
|
+
Formatter.render(Renderer.current) do |root|
|
|
101
|
+
root.keyed_list('description') do |node|
|
|
102
|
+
scoped_environments(options).each do |account|
|
|
103
|
+
account.each_orphaned_backup do |backup|
|
|
104
|
+
created_at = backup.created_at
|
|
105
|
+
copied_from = backup.copied_from
|
|
106
|
+
break if created_at < min_created_at && !copied_from
|
|
107
|
+
node.object do |n|
|
|
108
|
+
ResourceFormatter.inject_backup(
|
|
109
|
+
n, backup, include_db: true
|
|
110
|
+
)
|
|
111
|
+
end
|
|
83
112
|
end
|
|
84
113
|
end
|
|
85
114
|
end
|
|
86
115
|
end
|
|
87
116
|
end
|
|
88
117
|
|
|
89
|
-
desc 'backup:purge BACKUP_ID',
|
|
118
|
+
desc 'backup:purge BACKUP_ID',
|
|
119
|
+
'Permanently delete a backup and any copies of it'
|
|
90
120
|
define_method 'backup:purge' do |backup_id|
|
|
91
121
|
backup = Aptible::Api::Backup.find(backup_id, token: fetch_token)
|
|
92
122
|
raise Thor::Error, "Backup ##{backup_id} not found" if backup.nil?
|
|
@@ -53,13 +53,15 @@ module Aptible
|
|
|
53
53
|
|
|
54
54
|
desc 'db:create HANDLE ' \
|
|
55
55
|
'[--type TYPE] [--version VERSION] ' \
|
|
56
|
-
'[--container-size SIZE_MB] [--disk-size SIZE_GB]'
|
|
56
|
+
'[--container-size SIZE_MB] [--disk-size SIZE_GB] ' \
|
|
57
|
+
'[--key-arn KEY_ARN]',
|
|
57
58
|
'Create a new database'
|
|
58
59
|
option :type, type: :string
|
|
59
60
|
option :version, type: :string
|
|
60
61
|
option :container_size, type: :numeric
|
|
61
62
|
option :size, type: :numeric
|
|
62
63
|
option :disk_size, default: 10, type: :numeric
|
|
64
|
+
option :key_arn, type: :string
|
|
63
65
|
option :environment
|
|
64
66
|
define_method 'db:create' do |handle|
|
|
65
67
|
account = ensure_environment(options)
|
|
@@ -67,7 +69,8 @@ module Aptible
|
|
|
67
69
|
db_opts = {
|
|
68
70
|
handle: handle,
|
|
69
71
|
initial_container_size: options[:container_size],
|
|
70
|
-
initial_disk_size: options[:disk_size] || options[:size]
|
|
72
|
+
initial_disk_size: options[:disk_size] || options[:size],
|
|
73
|
+
current_kms_arn: options[:key_arn]
|
|
71
74
|
}.delete_if { |_, v| v.nil? }
|
|
72
75
|
|
|
73
76
|
CLI.logger.warn([
|
|
@@ -122,20 +125,41 @@ module Aptible
|
|
|
122
125
|
end
|
|
123
126
|
|
|
124
127
|
desc 'db:replicate HANDLE REPLICA_HANDLE ' \
|
|
125
|
-
'[--container-size SIZE_MB] [--disk-size SIZE_GB]'
|
|
128
|
+
'[--container-size SIZE_MB] [--disk-size SIZE_GB] ' \
|
|
129
|
+
'[--logical --version VERSION] [--key-arn KEY_ARN]',
|
|
126
130
|
'Create a replica/follower of a database'
|
|
127
131
|
option :environment
|
|
128
132
|
option :container_size, type: :numeric
|
|
129
133
|
option :size, type: :numeric
|
|
130
134
|
option :disk_size, type: :numeric
|
|
135
|
+
option :logical, type: :boolean
|
|
136
|
+
option :version, type: :string
|
|
137
|
+
option :key_arn, type: :string
|
|
131
138
|
define_method 'db:replicate' do |source_handle, dest_handle|
|
|
132
139
|
source = ensure_database(options.merge(db: source_handle))
|
|
140
|
+
|
|
141
|
+
if options[:logical]
|
|
142
|
+
if source.type != 'postgresql'
|
|
143
|
+
raise Thor::Error, 'Logical replication only works for ' \
|
|
144
|
+
'PostgreSQL'
|
|
145
|
+
end
|
|
146
|
+
if options[:version]
|
|
147
|
+
image = find_database_image(source.type, options[:version])
|
|
148
|
+
else
|
|
149
|
+
raise Thor::Error, '--version is required for logical ' \
|
|
150
|
+
'replication'
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
133
154
|
CLI.logger.info "Replicating #{source_handle}..."
|
|
134
155
|
|
|
135
156
|
opts = {
|
|
136
157
|
environment: options[:environment],
|
|
137
158
|
container_size: options[:container_size],
|
|
138
|
-
size: options[:disk_size] || options[:size]
|
|
159
|
+
size: options[:disk_size] || options[:size],
|
|
160
|
+
logical: options[:logical],
|
|
161
|
+
database_image: image || nil,
|
|
162
|
+
key_arn: options[:key_arn]
|
|
139
163
|
}.delete_if { |_, v| v.nil? }
|
|
140
164
|
|
|
141
165
|
CLI.logger.warn([
|
data/lib/aptible/cli/version.rb
CHANGED
|
@@ -2,14 +2,17 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Aptible::CLI::Agent do
|
|
4
4
|
let(:token) { 'some-token' }
|
|
5
|
-
let(:account) { Fabricate(:account, handle: 'test') }
|
|
5
|
+
let(:account) { Fabricate(:account, handle: 'test', id: 1) }
|
|
6
6
|
let(:alt_account) { Fabricate(:account, handle: 'alt') }
|
|
7
7
|
let(:database) { Fabricate(:database, account: account, handle: 'some-db') }
|
|
8
8
|
let!(:backup) do
|
|
9
9
|
# created_at: 2016-06-14 13:24:11 +0000
|
|
10
10
|
Fabricate(
|
|
11
11
|
:backup,
|
|
12
|
-
|
|
12
|
+
database_with_deleted: database,
|
|
13
|
+
created_at: Time.at(1465910651),
|
|
14
|
+
account: account,
|
|
15
|
+
id: 1
|
|
13
16
|
)
|
|
14
17
|
end
|
|
15
18
|
|
|
@@ -179,6 +182,50 @@ describe Aptible::CLI::Agent do
|
|
|
179
182
|
end
|
|
180
183
|
end
|
|
181
184
|
|
|
185
|
+
describe '#backup:orphaned' do
|
|
186
|
+
before { allow(Aptible::Api::Account).to receive(:all) { [account] } }
|
|
187
|
+
before do
|
|
188
|
+
m = allow(account).to receive(:each_orphaned_backup)
|
|
189
|
+
ages = [
|
|
190
|
+
1.day, 2.days, 3.days, 4.days,
|
|
191
|
+
5.days, 2.weeks, 3.weeks, 1.month,
|
|
192
|
+
1.year
|
|
193
|
+
]
|
|
194
|
+
ages.each do |age|
|
|
195
|
+
b = Fabricate(:backup, database: database, created_at: age.ago,
|
|
196
|
+
account: account)
|
|
197
|
+
allow(b).to receive(:database_with_deleted).and_return(database)
|
|
198
|
+
m.and_yield(b)
|
|
199
|
+
b
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
before { subject.options = { max_age: '1w' } }
|
|
203
|
+
|
|
204
|
+
it 'can show a subset of backups' do
|
|
205
|
+
subject.send('backup:orphaned')
|
|
206
|
+
puts captured_output_text
|
|
207
|
+
expect(captured_output_text.split("\n").size).to eq(5)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'allows scoping via environment' do
|
|
211
|
+
subject.options = { max_age: '1w', environment: database.account.handle }
|
|
212
|
+
subject.send('backup:orphaned')
|
|
213
|
+
expect(captured_output_text.split("\n").size).to eq(5)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it 'shows more backups if requested' do
|
|
217
|
+
subject.options = { max_age: '2y' }
|
|
218
|
+
subject.send('backup:orphaned')
|
|
219
|
+
expect(captured_output_text.split("\n").size).to eq(9)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'errors out if max_age is invalid' do
|
|
223
|
+
subject.options = { max_age: 'foobar' }
|
|
224
|
+
expect { subject.send('backup:orphaned') }
|
|
225
|
+
.to raise_error(Thor::Error, 'Invalid age: foobar')
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
182
229
|
describe '#backup:purge' do
|
|
183
230
|
it 'fails if the backup cannot be found' do
|
|
184
231
|
expect(Aptible::Api::Backup).to receive(:find)
|
|
@@ -13,7 +13,9 @@ describe Aptible::CLI::Agent do
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
let(:handle) { 'foobar' }
|
|
16
|
-
let(:
|
|
16
|
+
let(:stack) { Fabricate(:stack, internal_domain: 'aptible.in') }
|
|
17
|
+
let(:account) { Fabricate(:account, stack: stack) }
|
|
18
|
+
let(:database) { Fabricate(:database, handle: handle, account: account) }
|
|
17
19
|
let(:socat_helper) { SocatHelperMock.new(port: 4242) }
|
|
18
20
|
|
|
19
21
|
describe '#db:create' do
|
|
@@ -519,6 +521,69 @@ describe Aptible::CLI::Agent do
|
|
|
519
521
|
expect { subject.send('db:replicate', 'nope', 'replica') }
|
|
520
522
|
.to raise_error(Thor::Error, 'Could not find database nope')
|
|
521
523
|
end
|
|
524
|
+
|
|
525
|
+
it 'allows logical replication of a database with --version set' do
|
|
526
|
+
master = Fabricate(:database, handle: 'master')
|
|
527
|
+
databases << master
|
|
528
|
+
replica = Fabricate(:database,
|
|
529
|
+
account: master.account,
|
|
530
|
+
handle: 'replica')
|
|
531
|
+
|
|
532
|
+
dbimg = Fabricate(:database_image,
|
|
533
|
+
type: 'postgresql',
|
|
534
|
+
version: 10,
|
|
535
|
+
docker_repo: 'aptible/postgresql:10')
|
|
536
|
+
|
|
537
|
+
expect(subject).to receive(:find_database_image).with('postgresql', 10)
|
|
538
|
+
.and_return(dbimg)
|
|
539
|
+
|
|
540
|
+
op = Fabricate(:operation)
|
|
541
|
+
|
|
542
|
+
params = { type: 'replicate_logical', handle: 'replica',
|
|
543
|
+
docker_ref: dbimg.docker_repo }
|
|
544
|
+
expect(master).to receive(:create_operation!)
|
|
545
|
+
.with(**params).and_return(op)
|
|
546
|
+
|
|
547
|
+
expect(subject).to receive(:attach_to_operation_logs).with(op) do
|
|
548
|
+
databases << replica
|
|
549
|
+
replica
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
provision = Fabricate(:operation)
|
|
553
|
+
|
|
554
|
+
expect(replica).to receive_message_chain(:operations, :last)
|
|
555
|
+
.and_return(provision)
|
|
556
|
+
|
|
557
|
+
expect(subject).to receive(:attach_to_operation_logs).with(provision)
|
|
558
|
+
|
|
559
|
+
expect(replica).to receive(:reload).and_return(replica)
|
|
560
|
+
|
|
561
|
+
subject.options = { logical: true, version: 10 }
|
|
562
|
+
subject.send('db:replicate', 'master', 'replica')
|
|
563
|
+
|
|
564
|
+
expect(captured_logs).to match(/replicating master/i)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
it 'fails if logical replication requested without --version' do
|
|
568
|
+
master = Fabricate(:database, handle: 'master', type: 'postgresql')
|
|
569
|
+
databases << master
|
|
570
|
+
|
|
571
|
+
subject.options = { type: 'replicate', handle: 'replica', logical: true }
|
|
572
|
+
expect { subject.send('db:replicate', 'master', 'replica') }
|
|
573
|
+
.to raise_error(Thor::Error, '--version is required for logical ' \
|
|
574
|
+
'replication')
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
it 'fails if logical replication requested for non-postgres db' do
|
|
578
|
+
master = Fabricate(:database, handle: 'master', type: 'mysql')
|
|
579
|
+
databases << master
|
|
580
|
+
|
|
581
|
+
subject.options = { type: 'replicate', handle: 'replica',
|
|
582
|
+
logical: true, version: 10 }
|
|
583
|
+
expect { subject.send('db:replicate', 'master', 'replica') }
|
|
584
|
+
.to raise_error(Thor::Error, 'Logical replication only works for ' \
|
|
585
|
+
'PostgreSQL')
|
|
586
|
+
end
|
|
522
587
|
end
|
|
523
588
|
|
|
524
589
|
describe '#db:dump' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,14 +3,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
|
3
3
|
|
|
4
4
|
Bundler.require :development
|
|
5
5
|
|
|
6
|
-
require 'simplecov'
|
|
7
|
-
SimpleCov.start
|
|
8
|
-
|
|
9
|
-
if ENV['CI']
|
|
10
|
-
require 'codecov'
|
|
11
|
-
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
|
12
|
-
end
|
|
13
|
-
|
|
14
6
|
# Load shared spec files
|
|
15
7
|
Dir["#{File.dirname(__FILE__)}/shared/**/*.rb"].each do |file|
|
|
16
8
|
require file
|
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.16.
|
|
4
|
+
version: 0.16.9
|
|
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: 2020-
|
|
11
|
+
date: 2020-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aptible-resource
|
|
@@ -30,28 +30,28 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '1.
|
|
33
|
+
version: '1.2'
|
|
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.
|
|
40
|
+
version: '1.2'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: aptible-auth
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: 1.2.3
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: 1.2.3
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: aptible-billing
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -359,7 +359,7 @@ homepage: https://github.com/aptible/aptible-cli
|
|
|
359
359
|
licenses:
|
|
360
360
|
- MIT
|
|
361
361
|
metadata: {}
|
|
362
|
-
post_install_message:
|
|
362
|
+
post_install_message:
|
|
363
363
|
rdoc_options: []
|
|
364
364
|
require_paths:
|
|
365
365
|
- lib
|
|
@@ -375,7 +375,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
375
375
|
version: '0'
|
|
376
376
|
requirements: []
|
|
377
377
|
rubygems_version: 3.0.3
|
|
378
|
-
signing_key:
|
|
378
|
+
signing_key:
|
|
379
379
|
specification_version: 4
|
|
380
380
|
summary: Command-line interface for Aptible services
|
|
381
381
|
test_files:
|