aptible-cli 0.14.1 → 0.15.0
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/README.md +10 -1
- data/aptible-cli.gemspec +1 -0
- data/bin/aptible +9 -5
- data/lib/aptible/cli.rb +36 -0
- data/lib/aptible/cli/agent.rb +10 -6
- data/lib/aptible/cli/error.rb +6 -0
- data/lib/aptible/cli/formatter.rb +21 -0
- data/lib/aptible/cli/formatter/grouped_keyed_list.rb +54 -0
- data/lib/aptible/cli/formatter/keyed_list.rb +25 -0
- data/lib/aptible/cli/formatter/keyed_object.rb +16 -0
- data/lib/aptible/cli/formatter/list.rb +33 -0
- data/lib/aptible/cli/formatter/node.rb +8 -0
- data/lib/aptible/cli/formatter/object.rb +38 -0
- data/lib/aptible/cli/formatter/root.rb +46 -0
- data/lib/aptible/cli/formatter/value.rb +25 -0
- data/lib/aptible/cli/helpers/app.rb +1 -0
- data/lib/aptible/cli/helpers/database.rb +22 -6
- data/lib/aptible/cli/helpers/operation.rb +3 -2
- data/lib/aptible/cli/helpers/tunnel.rb +1 -3
- data/lib/aptible/cli/helpers/vhost.rb +9 -46
- data/lib/aptible/cli/renderer.rb +26 -0
- data/lib/aptible/cli/renderer/base.rb +8 -0
- data/lib/aptible/cli/renderer/json.rb +26 -0
- data/lib/aptible/cli/renderer/text.rb +99 -0
- data/lib/aptible/cli/resource_formatter.rb +136 -0
- data/lib/aptible/cli/subcommands/apps.rb +26 -14
- data/lib/aptible/cli/subcommands/backup.rb +22 -4
- data/lib/aptible/cli/subcommands/config.rb +15 -11
- data/lib/aptible/cli/subcommands/db.rb +82 -31
- data/lib/aptible/cli/subcommands/deploy.rb +1 -1
- data/lib/aptible/cli/subcommands/endpoints.rb +11 -8
- data/lib/aptible/cli/subcommands/operation.rb +2 -1
- data/lib/aptible/cli/subcommands/rebuild.rb +1 -1
- data/lib/aptible/cli/subcommands/restart.rb +1 -1
- data/lib/aptible/cli/subcommands/services.rb +8 -9
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/agent_spec.rb +11 -14
- data/spec/aptible/cli/formatter_spec.rb +4 -0
- data/spec/aptible/cli/renderer/json_spec.rb +63 -0
- data/spec/aptible/cli/renderer/text_spec.rb +150 -0
- data/spec/aptible/cli/resource_formatter_spec.rb +113 -0
- data/spec/aptible/cli/subcommands/apps_spec.rb +144 -28
- data/spec/aptible/cli/subcommands/backup_spec.rb +37 -16
- data/spec/aptible/cli/subcommands/config_spec.rb +95 -0
- data/spec/aptible/cli/subcommands/db_spec.rb +185 -93
- data/spec/aptible/cli/subcommands/endpoints_spec.rb +10 -8
- data/spec/aptible/cli/subcommands/operation_spec.rb +0 -1
- data/spec/aptible/cli/subcommands/rebuild_spec.rb +17 -0
- data/spec/aptible/cli/subcommands/services_spec.rb +8 -12
- data/spec/aptible/cli_spec.rb +31 -0
- data/spec/fabricators/account_fabricator.rb +11 -0
- data/spec/fabricators/app_fabricator.rb +15 -0
- data/spec/fabricators/configuration_fabricator.rb +8 -0
- data/spec/fabricators/database_image_fabricator.rb +17 -0
- data/spec/fabricators/operation_fabricator.rb +1 -0
- data/spec/fabricators/service_fabricator.rb +4 -0
- data/spec/spec_helper.rb +63 -1
- metadata +55 -4
- data/spec/aptible/cli/helpers/vhost_spec.rb +0 -105
@@ -11,12 +11,19 @@ module Aptible
|
|
11
11
|
desc 'apps', 'List all applications'
|
12
12
|
option :environment
|
13
13
|
def apps
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
Formatter.render(Renderer.current) do |root|
|
15
|
+
root.grouped_keyed_list(
|
16
|
+
{ 'environment' => 'handle' },
|
17
|
+
'handle'
|
18
|
+
) do |node|
|
19
|
+
scoped_environments(options).each do |account|
|
20
|
+
account.each_app do |app|
|
21
|
+
node.object do |n|
|
22
|
+
ResourceFormatter.inject_app(n, app, account)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
18
26
|
end
|
19
|
-
say ''
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
@@ -29,8 +36,13 @@ module Aptible
|
|
29
36
|
if app.errors.any?
|
30
37
|
raise Thor::Error, app.errors.full_messages.first
|
31
38
|
else
|
32
|
-
|
33
|
-
|
39
|
+
CLI.logger.info "App #{handle} created!"
|
40
|
+
|
41
|
+
Formatter.render(Renderer.current) do |root|
|
42
|
+
root.object do |o|
|
43
|
+
o.value('git_remote', app.git_repo)
|
44
|
+
end
|
45
|
+
end
|
34
46
|
end
|
35
47
|
end
|
36
48
|
|
@@ -56,9 +68,9 @@ module Aptible
|
|
56
68
|
# Noop
|
57
69
|
when 1
|
58
70
|
if container_count.nil?
|
59
|
-
m =
|
60
|
-
|
61
|
-
|
71
|
+
m = 'Passing container count as a positional ' \
|
72
|
+
'argument is deprecated, use --container-count'
|
73
|
+
CLI.logger.warn(m)
|
62
74
|
container_count = Integer(more.first)
|
63
75
|
else
|
64
76
|
raise Thor::Error, 'Container count was passed via both ' \
|
@@ -77,9 +89,9 @@ module Aptible
|
|
77
89
|
|
78
90
|
if options[:size]
|
79
91
|
if container_size.nil?
|
80
|
-
m =
|
81
|
-
|
82
|
-
|
92
|
+
m = 'Passing container size via the --size keyword ' \
|
93
|
+
'argument is deprecated, use --container-size'
|
94
|
+
CLI.logger.warn(m)
|
83
95
|
container_size = options[:size]
|
84
96
|
else
|
85
97
|
raise Thor::Error, 'Container size was passed via both ' \
|
@@ -106,7 +118,7 @@ module Aptible
|
|
106
118
|
app_options
|
107
119
|
define_method 'apps:deprovision' do
|
108
120
|
app = ensure_app(options)
|
109
|
-
|
121
|
+
CLI.logger.info "Deprovisioning #{app.handle}..."
|
110
122
|
app.create_operation!(type: 'deprovision')
|
111
123
|
end
|
112
124
|
end
|
@@ -40,8 +40,13 @@ module Aptible
|
|
40
40
|
}.delete_if { |_, v| v.nil? }
|
41
41
|
|
42
42
|
operation = backup.create_operation!(opts)
|
43
|
-
|
43
|
+
CLI.logger.info "Restoring backup into #{handle}"
|
44
44
|
attach_to_operation_logs(operation)
|
45
|
+
|
46
|
+
account = destination_account || backup.account
|
47
|
+
|
48
|
+
database = databases_from_handle(handle, account).first
|
49
|
+
render_database(database, account)
|
45
50
|
end
|
46
51
|
|
47
52
|
desc 'backup:list DB_HANDLE', 'List backups for a database'
|
@@ -55,9 +60,22 @@ module Aptible
|
|
55
60
|
min_created_at = Time.now - age
|
56
61
|
|
57
62
|
database = ensure_database(options.merge(db: handle))
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
|
64
|
+
Formatter.render(Renderer.current) do |root|
|
65
|
+
root.keyed_list('description') do |l|
|
66
|
+
database.each_backup do |backup|
|
67
|
+
break if backup.created_at < min_created_at
|
68
|
+
description = "#{backup.id}: #{backup.created_at}, " \
|
69
|
+
"#{backup.aws_region}"
|
70
|
+
|
71
|
+
l.object do |o|
|
72
|
+
o.value('id', backup.id)
|
73
|
+
o.value('description', description)
|
74
|
+
o.value('created_at', backup.created_at)
|
75
|
+
o.value('region', backup.aws_region)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
61
79
|
end
|
62
80
|
end
|
63
81
|
end
|
@@ -14,8 +14,19 @@ module Aptible
|
|
14
14
|
def config
|
15
15
|
app = ensure_app(options)
|
16
16
|
config = app.current_configuration
|
17
|
-
env = config ? config.env :
|
18
|
-
|
17
|
+
env = config ? config.env : {}
|
18
|
+
|
19
|
+
Formatter.render(Renderer.current) do |root|
|
20
|
+
root.keyed_list('shell_export') do |list|
|
21
|
+
env.each_pair do |k, v|
|
22
|
+
list.object do |node|
|
23
|
+
node.value('key', k)
|
24
|
+
node.value('value', v)
|
25
|
+
node.value('shell_export', "#{k}=#{Shellwords.escape(v)}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
19
30
|
end
|
20
31
|
|
21
32
|
desc 'config:add', 'Add an ENV variable to an app'
|
@@ -25,7 +36,7 @@ module Aptible
|
|
25
36
|
app = ensure_app(options)
|
26
37
|
env = extract_env(args)
|
27
38
|
operation = app.create_operation!(type: 'configure', env: env)
|
28
|
-
|
39
|
+
CLI.logger.info 'Updating configuration and restarting app...'
|
29
40
|
attach_to_operation_logs(operation)
|
30
41
|
end
|
31
42
|
|
@@ -45,7 +56,7 @@ module Aptible
|
|
45
56
|
[arg, '']
|
46
57
|
end]
|
47
58
|
operation = app.create_operation!(type: 'configure', env: env)
|
48
|
-
|
59
|
+
CLI.logger.info 'Updating configuration and restarting app...'
|
49
60
|
attach_to_operation_logs(operation)
|
50
61
|
end
|
51
62
|
|
@@ -54,13 +65,6 @@ module Aptible
|
|
54
65
|
define_method 'config:unset' do |*args|
|
55
66
|
send('config:rm', *args)
|
56
67
|
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def formatted_config(env)
|
61
|
-
env = Hash[env.sort]
|
62
|
-
env.map { |k, v| "#{k}=#{Shellwords.escape(v)}" }.join("\n")
|
63
|
-
end
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
@@ -15,28 +15,73 @@ module Aptible
|
|
15
15
|
desc 'db:list', 'List all databases'
|
16
16
|
option :environment
|
17
17
|
define_method 'db:list' do
|
18
|
-
|
19
|
-
|
18
|
+
Formatter.render(Renderer.current) do |root|
|
19
|
+
root.grouped_keyed_list(
|
20
|
+
{ 'environment' => 'handle' },
|
21
|
+
'handle'
|
22
|
+
) do |node|
|
23
|
+
scoped_environments(options).each do |account|
|
24
|
+
account.each_database do |db|
|
25
|
+
node.object do |n|
|
26
|
+
ResourceFormatter.inject_database(n, db, account)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
22
33
|
|
23
|
-
desc 'db:
|
24
|
-
|
34
|
+
desc 'db:versions', 'List available database versions'
|
35
|
+
define_method 'db:versions' do
|
36
|
+
Formatter.render(Renderer.current) do |root|
|
37
|
+
root.grouped_keyed_list('type', 'version') do |node|
|
38
|
+
Aptible::Api::DatabaseImage.all(
|
39
|
+
token: fetch_token
|
40
|
+
).each do |database_image|
|
41
|
+
node.object do |n|
|
42
|
+
n.value('type', database_image.type)
|
43
|
+
n.value('version', database_image.version)
|
44
|
+
n.value('default', database_image.default)
|
45
|
+
n.value('description', database_image.description)
|
46
|
+
n.value('docker_repo', database_image.default)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'db:create HANDLE ' \
|
54
|
+
'[--type TYPE] [--version VERSION] ' \
|
55
|
+
'[--container-size SIZE_MB] [--size SIZE_GB]',
|
25
56
|
'Create a new database'
|
26
|
-
option :type,
|
57
|
+
option :type, type: :string
|
58
|
+
option :version, type: :string
|
27
59
|
option :container_size, type: :numeric
|
28
60
|
option :size, default: 10, type: :numeric
|
29
61
|
option :environment
|
30
62
|
define_method 'db:create' do |handle|
|
31
|
-
|
63
|
+
account = ensure_environment(options)
|
32
64
|
|
33
65
|
db_opts = {
|
34
66
|
handle: handle,
|
35
|
-
type: options[:type],
|
36
67
|
initial_container_size: options[:container_size],
|
37
68
|
initial_disk_size: options[:size]
|
38
69
|
}.delete_if { |_, v| v.nil? }
|
39
|
-
|
70
|
+
|
71
|
+
type = options[:type]
|
72
|
+
version = options[:version]
|
73
|
+
|
74
|
+
if version && type
|
75
|
+
image = find_database_image(type, version)
|
76
|
+
db_opts[:type] = image.type
|
77
|
+
db_opts[:database_image] = image
|
78
|
+
elsif version
|
79
|
+
raise Thor::Error, '--type is required when passing --version'
|
80
|
+
else
|
81
|
+
db_opts[:type] = type || 'postgresql'
|
82
|
+
end
|
83
|
+
|
84
|
+
database = account.create_database!(db_opts)
|
40
85
|
|
41
86
|
op_opts = {
|
42
87
|
type: 'provision',
|
@@ -55,15 +100,17 @@ module Aptible
|
|
55
100
|
end
|
56
101
|
|
57
102
|
attach_to_operation_logs(op)
|
58
|
-
|
103
|
+
|
104
|
+
render_database(database.reload, account)
|
59
105
|
end
|
60
106
|
|
61
107
|
desc 'db:clone SOURCE DEST', 'Clone a database to create a new one'
|
62
108
|
option :environment
|
63
109
|
define_method 'db:clone' do |source_handle, dest_handle|
|
110
|
+
# TODO: Deprecate + recommend backup
|
64
111
|
source = ensure_database(options.merge(db: source_handle))
|
65
|
-
|
66
|
-
|
112
|
+
database = clone_database(source, dest_handle)
|
113
|
+
render_database(database, database.account)
|
67
114
|
end
|
68
115
|
|
69
116
|
desc 'db:dump HANDLE', 'Dump a remote database to file'
|
@@ -72,7 +119,7 @@ module Aptible
|
|
72
119
|
database = ensure_database(options.merge(db: handle))
|
73
120
|
with_postgres_tunnel(database) do |url|
|
74
121
|
filename = "#{handle}.dump"
|
75
|
-
|
122
|
+
CLI.logger.info "Dumping to #{filename}"
|
76
123
|
`pg_dump #{url} > #{filename}`
|
77
124
|
end
|
78
125
|
end
|
@@ -82,7 +129,7 @@ module Aptible
|
|
82
129
|
define_method 'db:execute' do |handle, sql_path|
|
83
130
|
database = ensure_database(options.merge(db: handle))
|
84
131
|
with_postgres_tunnel(database) do |url|
|
85
|
-
|
132
|
+
CLI.logger.info "Executing #{sql_path} against #{handle}"
|
86
133
|
`psql #{url} < #{sql_path}`
|
87
134
|
end
|
88
135
|
end
|
@@ -97,37 +144,37 @@ module Aptible
|
|
97
144
|
|
98
145
|
credential = find_credential(database, options[:type])
|
99
146
|
|
100
|
-
|
101
|
-
|
147
|
+
m = "Creating #{credential.type} tunnel to #{database.handle}..."
|
148
|
+
CLI.logger.info m
|
102
149
|
|
103
150
|
if options[:type].nil?
|
104
151
|
types = database.database_credentials.map(&:type)
|
105
152
|
unless types.empty?
|
106
153
|
valid = types.join(', ')
|
107
|
-
|
108
|
-
|
154
|
+
CLI.logger.info 'Use --type TYPE to specify a tunnel type'
|
155
|
+
CLI.logger.info "Valid types for #{database.handle}: #{valid}"
|
109
156
|
end
|
110
157
|
end
|
111
158
|
|
112
159
|
with_local_tunnel(credential, desired_port) do |tunnel_helper|
|
113
160
|
port = tunnel_helper.port
|
114
|
-
|
161
|
+
CLI.logger.info "Connect at #{local_url(credential, port)}"
|
115
162
|
|
116
163
|
uri = URI(local_url(credential, port))
|
117
164
|
db = uri.path.gsub(%r{^/}, '')
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
165
|
+
CLI.logger.info 'Or, use the following arguments:'
|
166
|
+
CLI.logger.info "* Host: #{uri.host}"
|
167
|
+
CLI.logger.info "* Port: #{uri.port}"
|
168
|
+
CLI.logger.info "* Username: #{uri.user}" unless uri.user.empty?
|
169
|
+
CLI.logger.info "* Password: #{uri.password}"
|
170
|
+
CLI.logger.info "* Database: #{db}" unless db.empty?
|
124
171
|
|
125
|
-
|
172
|
+
CLI.logger.info 'Connected. Ctrl-C to close connection.'
|
126
173
|
|
127
174
|
begin
|
128
175
|
tunnel_helper.wait
|
129
176
|
rescue Interrupt
|
130
|
-
|
177
|
+
CLI.logger.warn 'Closing tunnel'
|
131
178
|
end
|
132
179
|
end
|
133
180
|
end
|
@@ -136,7 +183,7 @@ module Aptible
|
|
136
183
|
option :environment
|
137
184
|
define_method 'db:deprovision' do |handle|
|
138
185
|
database = ensure_database(options.merge(db: handle))
|
139
|
-
|
186
|
+
CLI.logger.info "Deprovisioning #{database.handle}..."
|
140
187
|
database.create_operation!(type: 'deprovision')
|
141
188
|
end
|
142
189
|
|
@@ -144,7 +191,7 @@ module Aptible
|
|
144
191
|
option :environment
|
145
192
|
define_method 'db:backup' do |handle|
|
146
193
|
database = ensure_database(options.merge(db: handle))
|
147
|
-
|
194
|
+
CLI.logger.info "Backing up #{database.handle}..."
|
148
195
|
op = database.create_operation!(type: 'backup')
|
149
196
|
attach_to_operation_logs(op)
|
150
197
|
end
|
@@ -153,7 +200,7 @@ module Aptible
|
|
153
200
|
option :environment
|
154
201
|
define_method 'db:reload' do |handle|
|
155
202
|
database = ensure_database(options.merge(db: handle))
|
156
|
-
|
203
|
+
CLI.logger.info "Reloading #{database.handle}..."
|
157
204
|
op = database.create_operation!(type: 'reload')
|
158
205
|
attach_to_operation_logs(op)
|
159
206
|
end
|
@@ -173,7 +220,7 @@ module Aptible
|
|
173
220
|
disk_size: options[:size]
|
174
221
|
}.delete_if { |_, v| v.nil? }
|
175
222
|
|
176
|
-
|
223
|
+
CLI.logger.info "Restarting #{database.handle}..."
|
177
224
|
op = database.create_operation!(opts)
|
178
225
|
attach_to_operation_logs(op)
|
179
226
|
end
|
@@ -185,7 +232,11 @@ module Aptible
|
|
185
232
|
database = ensure_database(options.merge(db: handle))
|
186
233
|
credential = find_credential(database, options[:type])
|
187
234
|
|
188
|
-
|
235
|
+
Formatter.render(Renderer.current) do |root|
|
236
|
+
root.keyed_object('connection_url') do |node|
|
237
|
+
node.value('connection_url', credential.connection_url)
|
238
|
+
end
|
239
|
+
end
|
189
240
|
end
|
190
241
|
end
|
191
242
|
end
|
@@ -127,12 +127,15 @@ module Aptible
|
|
127
127
|
define_method 'endpoints:list' do
|
128
128
|
resource = ensure_app_or_database(options)
|
129
129
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
130
|
+
Formatter.render(Renderer.current) do |root|
|
131
|
+
root.list do |list|
|
132
|
+
each_service(resource) do |service|
|
133
|
+
service.each_vhost do |vhost|
|
134
|
+
list.object do |node|
|
135
|
+
ResourceFormatter.inject_vhost(node, vhost, service)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
136
139
|
end
|
137
140
|
end
|
138
141
|
end
|
@@ -143,7 +146,7 @@ module Aptible
|
|
143
146
|
app_or_database_options
|
144
147
|
define_method 'endpoints:deprovision' do |hostname|
|
145
148
|
resource = ensure_app_or_database(options)
|
146
|
-
vhost = find_vhost(
|
149
|
+
vhost = find_vhost(each_service(resource), hostname)
|
147
150
|
op = vhost.create_operation!(type: 'deprovision')
|
148
151
|
attach_to_operation_logs(op)
|
149
152
|
end
|
@@ -170,7 +173,7 @@ module Aptible
|
|
170
173
|
|
171
174
|
def modify_app_vhost(flags, options, hostname)
|
172
175
|
app = ensure_app(options)
|
173
|
-
vhost = find_vhost(
|
176
|
+
vhost = find_vhost(each_service(app), hostname)
|
174
177
|
vhost.update!(**flags.prepare(vhost.service.account, options))
|
175
178
|
provision_vhost_and_explain(vhost.service, vhost)
|
176
179
|
end
|
@@ -12,7 +12,8 @@ module Aptible
|
|
12
12
|
o = Aptible::Api::Operation.find(operation_id, token: fetch_token)
|
13
13
|
raise "Operation ##{operation_id} not found" if o.nil?
|
14
14
|
|
15
|
-
|
15
|
+
m = "Requesting cancellation on #{prettify_operation(o)}..."
|
16
|
+
CLI.logger.info m
|
16
17
|
o.update!(cancelled: true)
|
17
18
|
end
|
18
19
|
end
|