nex_client 0.1.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 +7 -0
- data/LICENSE +202 -0
- data/README.md +28 -0
- data/bin/nex-cli +7 -0
- data/lib/nex_client.rb +20 -0
- data/lib/nex_client/addon.rb +7 -0
- data/lib/nex_client/app.rb +24 -0
- data/lib/nex_client/base_resource.rb +19 -0
- data/lib/nex_client/cli.rb +348 -0
- data/lib/nex_client/commands.rb +17 -0
- data/lib/nex_client/commands/addons.rb +98 -0
- data/lib/nex_client/commands/apps.rb +301 -0
- data/lib/nex_client/commands/cube_instances.rb +89 -0
- data/lib/nex_client/commands/cube_templates.rb +44 -0
- data/lib/nex_client/commands/domains.rb +90 -0
- data/lib/nex_client/commands/helpers.rb +22 -0
- data/lib/nex_client/commands/organizations.rb +27 -0
- data/lib/nex_client/commands/racks.rb +90 -0
- data/lib/nex_client/commands/ssl_certificates.rb +100 -0
- data/lib/nex_client/commands/users.rb +49 -0
- data/lib/nex_client/compute_rack.rb +10 -0
- data/lib/nex_client/cube_instance.rb +21 -0
- data/lib/nex_client/cube_template.rb +8 -0
- data/lib/nex_client/domain.rb +7 -0
- data/lib/nex_client/gateway_rack.rb +7 -0
- data/lib/nex_client/organization.rb +5 -0
- data/lib/nex_client/resource_workflow.rb +17 -0
- data/lib/nex_client/routing_rack.rb +7 -0
- data/lib/nex_client/ssl_certificate.rb +7 -0
- data/lib/nex_client/storage_rack.rb +10 -0
- data/lib/nex_client/user.rb +5 -0
- data/lib/nex_client/version.rb +3 -0
- metadata +146 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module NexClient
|
5
|
+
module Commands
|
6
|
+
autoload :Addons, 'nex_client/commands/addons'
|
7
|
+
autoload :Apps, 'nex_client/commands/apps'
|
8
|
+
autoload :CubeInstances, 'nex_client/commands/cube_instances'
|
9
|
+
autoload :CubeTemplates, 'nex_client/commands/cube_templates'
|
10
|
+
autoload :Domains, 'nex_client/commands/domains'
|
11
|
+
autoload :Helpers, 'nex_client/commands/helpers'
|
12
|
+
autoload :Organizations, 'nex_client/commands/organizations'
|
13
|
+
autoload :Racks, 'nex_client/commands/racks'
|
14
|
+
autoload :SslCertificates, 'nex_client/commands/ssl_certificates'
|
15
|
+
autoload :Users, 'nex_client/commands/users'
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module NexClient
|
3
|
+
module Commands
|
4
|
+
module Addons
|
5
|
+
extend Helpers
|
6
|
+
|
7
|
+
ADDONS_TITLE = "Addons".colorize(:red)
|
8
|
+
ADDONS_HEADERS = ['id','name','status','service','app'].map(&:upcase)
|
9
|
+
|
10
|
+
def self.list(args,opts)
|
11
|
+
filters = {}
|
12
|
+
filters[:status] = opts.status || 'active'
|
13
|
+
filters[:service] = opts.service if opts.service.present?
|
14
|
+
|
15
|
+
# All option
|
16
|
+
filters = {} if opts.all
|
17
|
+
|
18
|
+
# Scope to specific app
|
19
|
+
filters[:'app.name'] = args.first if args.first.present?
|
20
|
+
|
21
|
+
# Create table
|
22
|
+
list = NexClient::Addon.includes(:app).where(filters).order('status')
|
23
|
+
self.display_addons(list)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create(args,opts)
|
27
|
+
svc_name,app_name = args
|
28
|
+
app = NexClient::App.find(name: app_name).first
|
29
|
+
|
30
|
+
# Display error
|
31
|
+
unless app
|
32
|
+
error("Error! Could not find app: #{app_name}")
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
addon = NexClient::Addon.new(service: svc_name)
|
37
|
+
addon.relationships.attributes = { app: { data: { type: 'apps', id: app.id } } }
|
38
|
+
addon.save
|
39
|
+
|
40
|
+
# Display errors if any
|
41
|
+
if addon.errors.any?
|
42
|
+
display_record_errors(addon)
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
# Display app
|
47
|
+
self.display_addons(NexClient::Addon.includes(:app).find(addon.id).first)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.destroy(args,opts)
|
51
|
+
name = args.first
|
52
|
+
e = NexClient::Addon.find(name: name).first
|
53
|
+
|
54
|
+
# Display error
|
55
|
+
unless e
|
56
|
+
error("Error! Could not find addon: #{name}")
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
|
60
|
+
# Ask confirmation
|
61
|
+
answer = ask("Enter the name of this addon to confirm: ")
|
62
|
+
unless answer == e.name
|
63
|
+
error("Aborting deletion...")
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
e.destroy
|
68
|
+
success("Successfully destroyed addon: #{name}")
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.display_addons(list)
|
72
|
+
table = Terminal::Table.new title: ADDONS_TITLE, headings: ADDONS_HEADERS do |t|
|
73
|
+
[list].flatten.compact.each do |e|
|
74
|
+
t.add_row(self.format_record(e))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
puts table
|
78
|
+
puts "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.format_record(record)
|
82
|
+
app = self.format_app(record)
|
83
|
+
[
|
84
|
+
record.id,
|
85
|
+
record.name,
|
86
|
+
record.status,
|
87
|
+
record.service,
|
88
|
+
app
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.format_app(record)
|
93
|
+
return '-' unless a = record.app
|
94
|
+
a.name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NexClient
|
4
|
+
module Commands
|
5
|
+
module Apps
|
6
|
+
extend Helpers
|
7
|
+
|
8
|
+
SCALING_DIRECTIONS = [:up,:down]
|
9
|
+
APPS_TITLE = "App Details".colorize(:green)
|
10
|
+
APPS_HEADERS = ['id','name','status','image','ssl','storage','preferred region','nodes','owner'].map(&:upcase)
|
11
|
+
|
12
|
+
VARS_TITLE = "Environment Variables".colorize(:blue)
|
13
|
+
VARS_HEADERS = ['key','value'].map(&:upcase)
|
14
|
+
|
15
|
+
SCM_TITLE = "Source Control Management".colorize(:yellow)
|
16
|
+
SCM_HEADERS = ['provider','repo','branch','last commit'].map(&:upcase)
|
17
|
+
|
18
|
+
def self.list(args,opts)
|
19
|
+
filters = {}
|
20
|
+
filters[:status] = [opts.status || 'active']
|
21
|
+
filters[:ssl_enabled] = opts.ssl if opts.ssl.present?
|
22
|
+
filters[:persistent_storage] = opts.storage if opts.storage.present?
|
23
|
+
filters[:'owner.handle'] = opts.owner if opts.owner.present?
|
24
|
+
|
25
|
+
# All option
|
26
|
+
filters = {} if opts.all
|
27
|
+
|
28
|
+
# Display
|
29
|
+
list = NexClient::App.includes(:owner).where(filters).order('status')
|
30
|
+
self.display_apps(list)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.info(args,opts)
|
34
|
+
name = args.first
|
35
|
+
e = NexClient::App.includes(:addons,:owner).find(name: name).first
|
36
|
+
|
37
|
+
# Display error
|
38
|
+
unless e
|
39
|
+
error("Error! Could not find app: #{name}")
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
# Display app details
|
44
|
+
self.display_apps(e)
|
45
|
+
|
46
|
+
# Display all vars
|
47
|
+
self.display_vars(e.all_vars)
|
48
|
+
|
49
|
+
# Display all vars
|
50
|
+
self.display_scm(e.scm)
|
51
|
+
|
52
|
+
# Display all addons
|
53
|
+
Addons.display_addons(e.addons.select { |a| a.status == 'active'})
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.create(args,opts)
|
57
|
+
image_info = args.first.split(':')
|
58
|
+
attrs = { image: image_info[0], image_tag: image_info[1] }
|
59
|
+
|
60
|
+
# Meta attributes
|
61
|
+
attrs[:ssl_enabled] = !opts.no_ssl if opts.no_ssl.present?
|
62
|
+
attrs[:persistent_storage] = opts.storage if opts.storage.present?
|
63
|
+
|
64
|
+
# Env variables via command line
|
65
|
+
if opts.env.present?
|
66
|
+
attrs[:vars] ||= {}
|
67
|
+
opts.env.each do |e|
|
68
|
+
key,val = e.split('=',2)
|
69
|
+
attrs[:vars][key] = val
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Env variables from file
|
74
|
+
if opts.env_file.present?
|
75
|
+
f = File.read(opts.env_file)
|
76
|
+
attrs[:vars] ||= {}
|
77
|
+
f.split('\n').each do |e|
|
78
|
+
key,val = e.split('=',2)
|
79
|
+
attrs[:vars][key] = val
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create the app
|
84
|
+
e = NexClient::App.new(attrs)
|
85
|
+
|
86
|
+
# Add owner if specified
|
87
|
+
if opts.owner.present?
|
88
|
+
o = NexClient::Organization.find(handle:opts.owner).first
|
89
|
+
unless o
|
90
|
+
error("Error! Could not find organization: #{opts.owner}")
|
91
|
+
return false
|
92
|
+
end
|
93
|
+
e.relationships.attributes = { owner: { data: { type: 'organizations', id: o.id } } }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Save the resource
|
97
|
+
e.save
|
98
|
+
|
99
|
+
# Display errors if any
|
100
|
+
if e.errors.any?
|
101
|
+
self.display_record_errors(e)
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
# Display app
|
106
|
+
self.display_apps(NexClient::App.includes(:owner).find(e.id).first)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.destroy(args,opts)
|
110
|
+
name = args.first
|
111
|
+
e = NexClient::App.find(name: name).first
|
112
|
+
|
113
|
+
# Display error
|
114
|
+
unless e
|
115
|
+
error("Error! Could not find app: #{name}")
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
|
119
|
+
# Ask confirmation
|
120
|
+
answer = ask("Enter the name of this app to confirm: ")
|
121
|
+
unless answer == e.name
|
122
|
+
error("Aborting deletion...")
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
e.destroy
|
127
|
+
success("Successfully destroyed app: #{name}")
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.restart(args,opts)
|
131
|
+
name = args.first
|
132
|
+
e = NexClient::App.find(name: name).first
|
133
|
+
|
134
|
+
# Display error
|
135
|
+
unless e
|
136
|
+
error("Error! Could not find app: #{name}")
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
|
140
|
+
# Perform
|
141
|
+
e.restart
|
142
|
+
|
143
|
+
# Display errors if any
|
144
|
+
if e.errors.any?
|
145
|
+
display_record_errors(e)
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
success("Initiated phased restart for app: #{name}...")
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.scale(direction,args,opts)
|
153
|
+
return false unless SCALING_DIRECTIONS.include?(direction.to_sym)
|
154
|
+
name = args.first
|
155
|
+
e = NexClient::App.find(name: name).first
|
156
|
+
|
157
|
+
# Display error
|
158
|
+
unless e
|
159
|
+
error("Error! Could not find app: #{name}")
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
|
163
|
+
# Scaling attributes
|
164
|
+
attrs = {}
|
165
|
+
count = opts.count || 1
|
166
|
+
attrs[:count] = count
|
167
|
+
attrs[:preferred_region] = opts.region if opts.region.present?
|
168
|
+
|
169
|
+
# Perform
|
170
|
+
e.send("scale_#{direction}",{data: { attributes: attrs } })
|
171
|
+
|
172
|
+
# Display errors if any
|
173
|
+
if e.errors.any?
|
174
|
+
display_record_errors(e)
|
175
|
+
return false
|
176
|
+
end
|
177
|
+
|
178
|
+
success("Successfully requested to scale #{name} #{direction} by #{count} #{'node'.pluralize(count)}...")
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.manage_vars(args,opts)
|
182
|
+
name = args.first
|
183
|
+
e = NexClient::App.find(name: name).first
|
184
|
+
|
185
|
+
# Display error
|
186
|
+
unless e
|
187
|
+
error("Error! Could not find app: #{name}")
|
188
|
+
return false
|
189
|
+
end
|
190
|
+
|
191
|
+
# Add/Delete vars
|
192
|
+
if opts.add.present? || opts.delete.present?
|
193
|
+
vars = (e.vars || {}).dup
|
194
|
+
|
195
|
+
# Add vars
|
196
|
+
(opts.add || []).each do |v|
|
197
|
+
key,val = v.split('=',2)
|
198
|
+
vars[key] = val
|
199
|
+
end
|
200
|
+
|
201
|
+
# Delete vars
|
202
|
+
(opts.delete || []).each { |k| vars.delete(k) }
|
203
|
+
|
204
|
+
# Update and reload the resource
|
205
|
+
e.update_attributes(vars: vars)
|
206
|
+
e = NexClient::App.find(e.id).first
|
207
|
+
end
|
208
|
+
|
209
|
+
# Display all vars
|
210
|
+
self.display_vars(e.all_vars)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.manage_scm(args,opts)
|
214
|
+
name = args.first
|
215
|
+
e = NexClient::App.find(name: name).first
|
216
|
+
|
217
|
+
# Display error
|
218
|
+
unless e
|
219
|
+
error("Error! Could not find app: #{name}")
|
220
|
+
return false
|
221
|
+
end
|
222
|
+
|
223
|
+
# Link SCM
|
224
|
+
if opts.link
|
225
|
+
provider,repo,branch = opts.link.split(':')
|
226
|
+
attrs = { provider: provider, config: { repo: repo, branch: branch } }
|
227
|
+
|
228
|
+
# Update and reload the resource
|
229
|
+
e.link_scm({data: { attributes: attrs } })
|
230
|
+
e = NexClient::App.find(e.id).first
|
231
|
+
end
|
232
|
+
|
233
|
+
# Unlink SCM
|
234
|
+
if !opts.link && opts.unlink
|
235
|
+
# Update and reload the resource
|
236
|
+
e.unlink_scm
|
237
|
+
e = NexClient::App.find(e.id).first
|
238
|
+
end
|
239
|
+
|
240
|
+
# Display all vars
|
241
|
+
self.display_scm(e.scm)
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.display_apps(list)
|
245
|
+
table = Terminal::Table.new title: APPS_TITLE, headings: APPS_HEADERS do |t|
|
246
|
+
[list].flatten.compact.each do |e|
|
247
|
+
t.add_row(self.format_record(e))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
puts table
|
251
|
+
puts "\n"
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.display_vars(list)
|
255
|
+
table = Terminal::Table.new title: VARS_TITLE, headings: VARS_HEADERS do |t|
|
256
|
+
[list].flatten.compact.each do |e|
|
257
|
+
e.each { |k,v| t.add_row([k,v]) }
|
258
|
+
end
|
259
|
+
end
|
260
|
+
puts table
|
261
|
+
puts "\n"
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.display_scm(list)
|
265
|
+
table = Terminal::Table.new title: SCM_TITLE, headings: SCM_HEADERS do |t|
|
266
|
+
[list].flatten.compact.each do |e|
|
267
|
+
t.add_row([e['provider'],e['repo'],e['branch'],e['last_commit'] || '- nothing pushed yet -'])
|
268
|
+
end
|
269
|
+
end
|
270
|
+
puts table
|
271
|
+
puts "\n"
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.format_record(record)
|
275
|
+
owner = self.format_owner(record)
|
276
|
+
node_count = self.format_node_count(record)
|
277
|
+
[
|
278
|
+
record.id,
|
279
|
+
record.name,
|
280
|
+
record.status,
|
281
|
+
record.image,
|
282
|
+
record.ssl_enabled,
|
283
|
+
record.persistent_storage,
|
284
|
+
record.preferred_region,
|
285
|
+
node_count,
|
286
|
+
owner
|
287
|
+
]
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.format_owner(record)
|
291
|
+
return '-' unless o = record.owner
|
292
|
+
"#{o.type[0]}:#{o.handle}"
|
293
|
+
end
|
294
|
+
|
295
|
+
def self.format_node_count(record)
|
296
|
+
return '-' unless record.node_count && record.max_node_count
|
297
|
+
"#{record.node_count}/#{record.max_node_count}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module NexClient
|
3
|
+
module Commands
|
4
|
+
module CubeInstances
|
5
|
+
extend Helpers
|
6
|
+
|
7
|
+
CUBES_TITLE = "Cube Instances".colorize(:blue)
|
8
|
+
CUBES_HEADERS = ['id','status','region','ssl','storage','SOA','ip','port','dns','cluster'].map(&:upcase)
|
9
|
+
|
10
|
+
def self.list(args,opts)
|
11
|
+
filters = {}
|
12
|
+
filters[:status] = opts.status || 'running'
|
13
|
+
filters[:soa_enabled] = opts.soa if opts.soa.present?
|
14
|
+
filters[:ssl_enabled] = opts.ssl if opts.ssl.present?
|
15
|
+
filters[:persistent_storage] = opts.storage if opts.storage.present?
|
16
|
+
|
17
|
+
# All option
|
18
|
+
filters = {} if opts.all
|
19
|
+
|
20
|
+
# Identification options
|
21
|
+
filters[:dns] = opts.dns if opts.dns.present?
|
22
|
+
filters[:'app.name'] = opts.app if opts.app.present?
|
23
|
+
filters[:'addon.name'] = opts.addon if opts.addon.present?
|
24
|
+
|
25
|
+
# Create table
|
26
|
+
list = NexClient::CubeInstance.includes(:cluster).where(filters).order('status')
|
27
|
+
self.display_cubes(list)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.trigger_action(action,args,opts)
|
31
|
+
name = args.first
|
32
|
+
e = NexClient::CubeInstance.find(name: name).first
|
33
|
+
|
34
|
+
# Display error
|
35
|
+
unless e
|
36
|
+
error("Error! Could not find cube: #{name}")
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Perform
|
41
|
+
e.send(action)
|
42
|
+
|
43
|
+
# Display errors if any
|
44
|
+
if e.errors.any?
|
45
|
+
display_record_errors(e)
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
|
49
|
+
success("Initiated #{action} for cube #{name}...")
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.display_cubes(list)
|
53
|
+
table = Terminal::Table.new title: CUBES_TITLE, headings: CUBES_HEADERS do |t|
|
54
|
+
[list].flatten.compact.each do |e|
|
55
|
+
t.add_row(self.format_record(e))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
puts table
|
59
|
+
puts "\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.format_record(record)
|
63
|
+
dns = self.format_dns(record)
|
64
|
+
cluster = self.format_cluster(record)
|
65
|
+
[
|
66
|
+
record.id,
|
67
|
+
record.status,
|
68
|
+
record.region,
|
69
|
+
record.ssl_enabled,
|
70
|
+
record.persistent_storage,
|
71
|
+
record.soa_enabled,
|
72
|
+
record.ip_address || '-',
|
73
|
+
record.port || '-',
|
74
|
+
dns,
|
75
|
+
cluster
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.format_dns(record)
|
80
|
+
record.dns || '-'
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.format_cluster(record)
|
84
|
+
return '-' unless o = record.cluster
|
85
|
+
"#{o.type.singularize}:#{o.name}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|