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.
@@ -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