com.proofpoint.galaxy.cli 0.6 → 0.7
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.
- data/lib/galaxy.rb +333 -72
- data/lib/galaxy/colorize.rb +23 -0
- data/lib/galaxy/shell.rb +6 -0
- data/lib/galaxy/table.rb +50 -0
- metadata +16 -2
data/lib/galaxy.rb
CHANGED
@@ -2,7 +2,23 @@
|
|
2
2
|
require 'optparse'
|
3
3
|
require 'httpclient'
|
4
4
|
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
require 'base64'
|
7
|
+
require 'digest/md5'
|
8
|
+
require 'ssh/key/signer'
|
5
9
|
require 'galaxy/version'
|
10
|
+
require 'galaxy/colorize'
|
11
|
+
require 'galaxy/shell'
|
12
|
+
require 'galaxy/table'
|
13
|
+
|
14
|
+
# stop http client from overriding the Date header
|
15
|
+
class HTTPClient
|
16
|
+
class Session
|
17
|
+
def set_header(req)
|
18
|
+
# do nothing
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
6
22
|
|
7
23
|
module Galaxy
|
8
24
|
GALAXY_VERSION = "0.1"
|
@@ -11,28 +27,89 @@ module Galaxy
|
|
11
27
|
:success => 0,
|
12
28
|
:no_slots => 1,
|
13
29
|
:unsupported => 3,
|
14
|
-
:invalid_usage => 64
|
30
|
+
:invalid_usage => 64,
|
31
|
+
:no_signing_identities => 80,
|
32
|
+
:general_error => 99
|
15
33
|
}
|
16
34
|
|
17
35
|
#
|
18
36
|
# Slot Information
|
19
37
|
#
|
20
38
|
class Slot
|
21
|
-
attr_reader :uuid, :host, :ip, :url, :binary, :config, :status
|
39
|
+
attr_reader :uuid, :short_id, :host, :ip, :url, :binary, :config, :status, :status_message, :path
|
22
40
|
|
23
|
-
def initialize(uuid, url, binary, config, status)
|
41
|
+
def initialize(uuid, short_id, url, binary, config, status, status_message, path)
|
24
42
|
@uuid = uuid
|
43
|
+
@short_id = short_id
|
25
44
|
@url = url
|
26
45
|
@binary = binary
|
27
46
|
@config = config
|
28
47
|
@status = status
|
29
|
-
|
30
|
-
@
|
31
|
-
|
48
|
+
@status_message = status_message
|
49
|
+
@path = path
|
50
|
+
|
51
|
+
unless url.nil?
|
52
|
+
@host = URI.parse(url).host unless url.nil?
|
53
|
+
@ip = IPSocket::getaddress(host)
|
54
|
+
end
|
55
|
+
|
56
|
+
@host ||= "unknown"
|
57
|
+
@ip ||= "unknown"
|
58
|
+
end
|
59
|
+
|
60
|
+
def columns(colors = false)
|
61
|
+
status = @status
|
62
|
+
status_message = @status_message
|
63
|
+
|
64
|
+
if colors
|
65
|
+
status = case status
|
66
|
+
when "RUNNING" then Colorize::colorize(status, :bright, :green)
|
67
|
+
when "STOPPED" then status
|
68
|
+
else status
|
69
|
+
end
|
70
|
+
status_message = Colorize::colorize(status_message, :red)
|
71
|
+
end
|
72
|
+
|
73
|
+
return [@short_id, @host, status, @binary, @config, status_message].map { |value| value || '' }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
#
|
77
|
+
# Agent Information
|
78
|
+
#
|
79
|
+
class Agent
|
80
|
+
attr_reader :agent_id, :host, :ip, :url, :status, :location, :path, :instance_type
|
81
|
+
|
82
|
+
def initialize(agent_id, status, url, location, instance_type)
|
83
|
+
@agent_id = agent_id
|
84
|
+
@url = url
|
85
|
+
@status = status
|
86
|
+
@path = path
|
87
|
+
|
88
|
+
unless url.nil?
|
89
|
+
@host = URI.parse(url).host unless url.nil?
|
90
|
+
@ip = IPSocket::getaddress(host)
|
91
|
+
end
|
92
|
+
|
93
|
+
@host ||= "unknown"
|
94
|
+
@ip ||= "unknown"
|
95
|
+
|
96
|
+
@location = location
|
97
|
+
@instance_type = instance_type
|
32
98
|
end
|
33
99
|
|
34
|
-
def
|
35
|
-
|
100
|
+
def columns(colors = false)
|
101
|
+
status = @status
|
102
|
+
|
103
|
+
if colors
|
104
|
+
status = case status
|
105
|
+
when "ONLINE" then Colorize::colorize(status, :bright, :green)
|
106
|
+
when "OFFLINE" then Colorize::colorize(status, :bright, :red)
|
107
|
+
when "PROVISIONING" then Colorize::colorize(status, :bright, :blue)
|
108
|
+
else status
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
return [@agent_id, @host, status, @instance_type, @location].map { |value| value || '' }
|
36
113
|
end
|
37
114
|
end
|
38
115
|
|
@@ -54,13 +131,9 @@ module Galaxy
|
|
54
131
|
coordinator_request(filter, options, :get)
|
55
132
|
end
|
56
133
|
|
57
|
-
def self.
|
134
|
+
def self.install(filter, options, args)
|
58
135
|
if args.size != 2 then
|
59
|
-
raise CommandError.new(:invalid_usage, "You must specify a binary and config to
|
60
|
-
end
|
61
|
-
if filter.empty? then
|
62
|
-
raise CommandError.new(:invalid_usage, "You must specify a filter when for assign.")
|
63
|
-
|
136
|
+
raise CommandError.new(:invalid_usage, "You must specify a binary and config to install.")
|
64
137
|
end
|
65
138
|
if args[0].start_with? '@'
|
66
139
|
config = args[0]
|
@@ -70,21 +143,44 @@ module Galaxy
|
|
70
143
|
config = args[1]
|
71
144
|
|
72
145
|
end
|
73
|
-
|
146
|
+
installation = {
|
74
147
|
:binary => binary,
|
75
148
|
:config => config
|
76
149
|
}
|
77
|
-
coordinator_request(filter, options, :
|
150
|
+
coordinator_request(filter, options, :post, nil, installation, true)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.upgrade(filter, options, args)
|
154
|
+
if args.size <= 0 || args.size > 2 then
|
155
|
+
raise CommandError.new(:invalid_usage, "You must specify a binary version or a config version for upgrade.")
|
156
|
+
end
|
157
|
+
if filter.empty? then
|
158
|
+
raise CommandError.new(:invalid_usage, "You must specify a filter when for upgrade.")
|
159
|
+
end
|
160
|
+
|
161
|
+
if args[0].start_with? '@'
|
162
|
+
config_version = args[0][1..-1]
|
163
|
+
binary_version = args[1] if args.size > 1
|
164
|
+
else
|
165
|
+
binary_version = args[0]
|
166
|
+
config_version = args[1][1..-1] if args.size > 1
|
167
|
+
|
168
|
+
end
|
169
|
+
versions = {}
|
170
|
+
versions[:binaryVersion] = binary_version if binary_version
|
171
|
+
versions[:configVersion] = config_version if config_version
|
172
|
+
|
173
|
+
coordinator_request(filter, options, :post, 'assignment', versions, true)
|
78
174
|
end
|
79
175
|
|
80
|
-
def self.
|
176
|
+
def self.terminate(filter, options, args)
|
81
177
|
if !args.empty? then
|
82
|
-
raise CommandError.new(:invalid_usage, "You can not pass arguments to
|
178
|
+
raise CommandError.new(:invalid_usage, "You can not pass arguments to terminate.")
|
83
179
|
end
|
84
180
|
if filter.empty? then
|
85
|
-
raise CommandError.new(:invalid_usage, "You must specify a filter when for
|
181
|
+
raise CommandError.new(:invalid_usage, "You must specify a filter when for terminate.")
|
86
182
|
end
|
87
|
-
coordinator_request(filter, options, :delete
|
183
|
+
coordinator_request(filter, options, :delete)
|
88
184
|
end
|
89
185
|
|
90
186
|
def self.start(filter, options, args)
|
@@ -130,56 +226,152 @@ module Galaxy
|
|
130
226
|
end
|
131
227
|
|
132
228
|
slot = slots.first
|
133
|
-
|
134
|
-
|
229
|
+
ssh = ENV['GALAXY_SSH_COMMAND'] || "ssh"
|
230
|
+
|
231
|
+
if slot.path.nil?
|
232
|
+
path = "$HOME"
|
233
|
+
else
|
234
|
+
path = Shell::quote(slot.path)
|
235
|
+
end
|
236
|
+
remote_command = "cd #{path}; #{options[:ssh_command]}"
|
237
|
+
command = "#{ssh} #{slot.host} -t #{Shell::quote(remote_command)}"
|
238
|
+
|
239
|
+
puts command if options[:debug]
|
240
|
+
system(command)
|
135
241
|
[]
|
136
242
|
end
|
137
243
|
|
138
|
-
|
244
|
+
def self.reset_to_actual(filter, options, args)
|
245
|
+
if !args.empty? then
|
246
|
+
raise CommandError.new(:invalid_usage, "You can not pass arguments to reset-to-actual.")
|
247
|
+
end
|
248
|
+
if filter.empty? then
|
249
|
+
raise CommandError.new(:invalid_usage, "You must specify a filter when for reset-to-actual.")
|
250
|
+
end
|
251
|
+
coordinator_request(filter, options, :delete, "expected-state")
|
252
|
+
end
|
139
253
|
|
140
|
-
def self.
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
254
|
+
def self.agent_show(filter, options, args)
|
255
|
+
if !args.empty? then
|
256
|
+
raise CommandError.new(:invalid_usage, "You can not pass arguments to agent show.")
|
257
|
+
end
|
258
|
+
if !filter.empty? then
|
259
|
+
raise CommandError.new(:invalid_usage, "You can not use filters with agent show.")
|
260
|
+
end
|
261
|
+
coordinator_agent_request(filter, options, :get)
|
262
|
+
end
|
146
263
|
|
147
|
-
|
148
|
-
|
264
|
+
def self.agent_add(filter, options, args)
|
265
|
+
if args.size > 1 then
|
266
|
+
raise CommandError.new(:invalid_usage, "Agent add only accepts one argument.")
|
267
|
+
end
|
268
|
+
if !filter.empty? then
|
269
|
+
raise CommandError.new(:invalid_usage, "You can not use filters with agent show.")
|
270
|
+
end
|
149
271
|
|
272
|
+
if args.size > 0 then
|
273
|
+
instance_type = args[0]
|
274
|
+
end
|
275
|
+
|
276
|
+
provisioning = {}
|
277
|
+
provisioning[:instanceType] = instance_type if instance_type
|
278
|
+
provisioning[:availabilityZone] = options[:availability_zone] if options[:availability_zone]
|
279
|
+
|
280
|
+
query = "count=#{options[:count] || 1}"
|
281
|
+
|
282
|
+
coordinator_agent_request(filter, options, :post, query, nil, provisioning, true)
|
283
|
+
end
|
284
|
+
|
285
|
+
private
|
286
|
+
|
287
|
+
def self.execute_request(method, uri, query, body, options, is_json)
|
150
288
|
# encode body as json if necessary
|
151
|
-
|
152
|
-
headers = {}
|
289
|
+
headers = []
|
153
290
|
if is_json
|
154
|
-
body =
|
155
|
-
headers['Content-Type'
|
291
|
+
body = body.to_json
|
292
|
+
headers += [['Content-Type', 'application/json']]
|
293
|
+
end
|
294
|
+
|
295
|
+
# add date header
|
296
|
+
request_time = Time.now
|
297
|
+
headers += [['Date', request_time.gmtime.strftime('%a, %d %b %Y %H:%M:%S GMT')]]
|
298
|
+
|
299
|
+
# remove query string if empty
|
300
|
+
query = nil if query.to_s.empty?
|
301
|
+
|
302
|
+
# build signature string
|
303
|
+
timestamp = request_time.to_i
|
304
|
+
method = method.to_s.upcase
|
305
|
+
relative_uri = URI.parse(uri).path + ('?' + query if query).to_s
|
306
|
+
body_md5 = Digest::MD5.hexdigest(body.to_s)
|
307
|
+
string_to_sign = [timestamp, method, relative_uri, body_md5].join("\n")
|
308
|
+
puts "string_to_sign:\n#{string_to_sign}\n--" if options[:debug]
|
309
|
+
|
310
|
+
# generate signature headers
|
311
|
+
signer = SSH::Key::Signer.new
|
312
|
+
signatures = signer.sign(string_to_sign)
|
313
|
+
if signatures.empty?
|
314
|
+
raise CommandError.new(:no_signing_identities, "No identities in SSH agent (use ssh-add)")
|
315
|
+
end
|
316
|
+
signatures.each do |sig|
|
317
|
+
fingerprint = sig.identity.fingerprint.delete(':')
|
318
|
+
signature = Base64.strict_encode64(sig.signature)
|
319
|
+
headers += [['Authorization', "Galaxy #{fingerprint}:#{signature}"]]
|
156
320
|
end
|
157
321
|
|
158
322
|
# log request in as a valid curl command if in debug mode
|
159
323
|
if options[:debug]
|
160
|
-
if
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
324
|
+
debug_uri = uri + ('?' + query if query).to_s
|
325
|
+
print "curl"
|
326
|
+
headers.each { |k,v| print " -H '#{k}: #{v}'" }
|
327
|
+
print " -X #{method}"
|
328
|
+
print " -d '#{body}'" if body
|
329
|
+
puts " '#{debug_uri}'"
|
330
|
+
puts
|
167
331
|
end
|
168
332
|
|
169
333
|
# execute request
|
170
|
-
response = HTTPClient.new.request(method, uri, query, body, headers)
|
171
|
-
|
172
|
-
|
173
|
-
slots_json = JSON.parse(response)
|
334
|
+
response = HTTPClient.new.request(method, uri, query, body, headers)
|
335
|
+
response_body = response.body if response.respond_to?(:body)
|
336
|
+
response_body = response_body.content if response_body.respond_to?(:content)
|
174
337
|
|
175
338
|
# log response if in debug mode
|
176
339
|
if options[:debug]
|
177
|
-
puts
|
340
|
+
puts
|
341
|
+
puts "#{response.status} #{response.reason}"
|
342
|
+
puts "--\n#{response_body.rstrip}\n--"
|
343
|
+
end
|
344
|
+
|
345
|
+
# check response code
|
346
|
+
if response.status / 100 != 2 then
|
347
|
+
raise CommandError.new(:general_error, response.reason || "Unknown error executing command")
|
178
348
|
end
|
349
|
+
response_body
|
350
|
+
end
|
351
|
+
|
352
|
+
def self.coordinator_request(filter, options, method, sub_path = nil, value = nil, is_json = false)
|
353
|
+
# build the uri
|
354
|
+
uri = options[:coordinator_url]
|
355
|
+
uri += '/' unless uri.end_with? '/'
|
356
|
+
uri += 'v1/slot/'
|
357
|
+
uri += sub_path unless sub_path.nil?
|
358
|
+
|
359
|
+
# create filter query
|
360
|
+
params = filter
|
361
|
+
# todo this arbitrary rename here is just wrong
|
362
|
+
params["limit"] <<= options[:count] unless options[:count].nil?
|
363
|
+
params["pretty"] <<= "true" unless options[:debug].nil?
|
364
|
+
query = params.map { |k, v| v.map { |v1| "#{k}=#{URI.escape(v1)}" }.join('&') }.join('&')
|
365
|
+
|
366
|
+
# execute request
|
367
|
+
response_body = execute_request(method, uri, query, value, options, is_json)
|
368
|
+
|
369
|
+
# parse response as json
|
370
|
+
slots_json = JSON.parse(response_body)
|
179
371
|
|
180
372
|
# convert parsed json into slot objects
|
181
373
|
slots = slots_json.map do |slot_json|
|
182
|
-
Slot.new(slot_json['id'], slot_json['self'], slot_json['binary'], slot_json['config'], slot_json['status'])
|
374
|
+
Slot.new(slot_json['id'], slot_json['shortId'], slot_json['self'], slot_json['binary'], slot_json['config'], slot_json['status'], slot_json['statusMessage'], slot_json['installPath'])
|
183
375
|
end
|
184
376
|
|
185
377
|
# verify response
|
@@ -189,19 +381,43 @@ module Galaxy
|
|
189
381
|
|
190
382
|
slots
|
191
383
|
end
|
384
|
+
|
385
|
+
def self.coordinator_agent_request(filter, options, method, query = '', sub_path = nil, value = nil, is_json = false)
|
386
|
+
# build the uri
|
387
|
+
uri = options[:coordinator_url]
|
388
|
+
uri += '/' unless uri.end_with? '/'
|
389
|
+
uri += 'v1/admin/agent'
|
390
|
+
uri += sub_path unless sub_path.nil?
|
391
|
+
|
392
|
+
query = '&pretty' unless options[:debug].nil?
|
393
|
+
|
394
|
+
# execute request
|
395
|
+
response_body = execute_request(method, uri, query, value, options, is_json)
|
396
|
+
|
397
|
+
# parse response as json
|
398
|
+
agents_json = JSON.parse(response_body)
|
399
|
+
|
400
|
+
# convert parsed json into slot objects
|
401
|
+
agents = agents_json.map do |agent_json|
|
402
|
+
Agent.new(agent_json['agentId'], agent_json['state'], agent_json['self'], agent_json['location'], agent_json['instanceType'])
|
403
|
+
end
|
404
|
+
|
405
|
+
return agents
|
406
|
+
end
|
192
407
|
end
|
193
408
|
|
194
409
|
class CLI
|
195
410
|
|
196
|
-
COMMANDS = [:show, :
|
411
|
+
COMMANDS = [:show, :install, :upgrade, :terminate, :start, :stop, :restart, :ssh, :reset_to_actual, :agent_show, :agent_add]
|
197
412
|
INITIAL_OPTIONS = {
|
198
|
-
:coordinator_url => ENV['GALAXY_COORDINATOR'] || 'http://localhost:64000'
|
413
|
+
:coordinator_url => ENV['GALAXY_COORDINATOR'] || 'http://localhost:64000',
|
414
|
+
:ssh_command => "bash --login"
|
199
415
|
}
|
200
416
|
|
201
417
|
def self.parse_command_line(args)
|
202
418
|
options = INITIAL_OPTIONS
|
203
419
|
|
204
|
-
filter = Hash.new
|
420
|
+
filter = Hash.new{[]}
|
205
421
|
|
206
422
|
option_parser = OptionParser.new do |opts|
|
207
423
|
opts.banner = "Usage: #{File.basename($0)} [options] <command>"
|
@@ -231,35 +447,46 @@ module Galaxy
|
|
231
447
|
opts.separator 'Filters:'
|
232
448
|
|
233
449
|
opts.on("-b", "--binary BINARY", "Select slots with a given binary") do |arg|
|
234
|
-
filter[:binary]
|
450
|
+
filter[:binary] <<= arg
|
235
451
|
end
|
236
452
|
|
237
453
|
opts.on("-c", "--config CONFIG", "Select slots with given configuration") do |arg|
|
238
|
-
filter[:config]
|
454
|
+
filter[:config] <<= arg
|
239
455
|
end
|
240
456
|
|
241
457
|
opts.on("-i", "--host HOST", "Select slots on the given hostname") do |arg|
|
242
|
-
filter[:host]
|
458
|
+
filter[:host] <<= arg
|
243
459
|
end
|
244
460
|
|
245
461
|
opts.on("-I", "--ip IP", "Select slots at the given IP address") do |arg|
|
246
|
-
filter[:ip]
|
462
|
+
filter[:ip] <<= arg
|
247
463
|
end
|
248
464
|
|
249
465
|
opts.on("-u", "--uuid SLOT_UUID", "Select slots with given slot uuid") do |arg|
|
250
|
-
filter[:uuid]
|
466
|
+
filter[:uuid] <<= arg
|
467
|
+
end
|
468
|
+
|
469
|
+
opts.on("--count count", "Number of instances to install or agents to provision") do |arg|
|
470
|
+
options[:count] = arg
|
251
471
|
end
|
252
472
|
|
253
|
-
opts.on("-
|
473
|
+
opts.on("--availability-zone AVAILABILITY_ZONE", "Availability zone to agent provisioning") do |arg|
|
474
|
+
options[:availability_zone] = arg
|
475
|
+
end
|
476
|
+
|
477
|
+
# todo find a better command line argument
|
478
|
+
opts.on("-x", "--ssh-command SSH_COMMAND", "Command to execute with ssh") do |arg|
|
479
|
+
options[:ssh_command] = arg
|
480
|
+
end
|
481
|
+
|
482
|
+
opts.on("-s", "--state STATE", "Select 'r{unning}', 's{topped}' or 'unknown' slots", [:running, :r, :stopped, :s, :unknown]) do |arg|
|
254
483
|
case arg
|
255
484
|
when :running, :r then
|
256
|
-
filter[:state]
|
485
|
+
filter[:state] <<= 'running'
|
257
486
|
when :stopped, :s then
|
258
|
-
filter[:state]
|
259
|
-
when :unassigned, :u then
|
260
|
-
filter[:state] = 'unassigned'
|
487
|
+
filter[:state] <<= 'stopped'
|
261
488
|
when :unknown then
|
262
|
-
filter[:state]
|
489
|
+
filter[:state] <<= 'unknown'
|
263
490
|
end
|
264
491
|
end
|
265
492
|
|
@@ -281,15 +508,27 @@ NOTES
|
|
281
508
|
option_parser.parse!(args)
|
282
509
|
|
283
510
|
puts options.map { |k, v| "#{k}=#{v}" }.join("\n") if options[:debug]
|
284
|
-
puts filter.map { |k, v| "#{k}=#{
|
511
|
+
puts filter.map { |k, v| v.map { |v1| "#{k}=#{URI.escape(v1)}" }.join('\n') }.join('\n') if options[:debug]
|
285
512
|
|
286
|
-
if args.length == 0
|
513
|
+
if args.length == 0 then
|
287
514
|
puts option_parser
|
288
515
|
exit EXIT_CODES[:success]
|
289
516
|
end
|
290
517
|
|
518
|
+
args[0] = args[0].gsub('-', '_');
|
291
519
|
command = args[0].to_sym
|
292
520
|
|
521
|
+
is_agent = false
|
522
|
+
if command == :agent then
|
523
|
+
args = args.drop(1)
|
524
|
+
if args.length == 0 then
|
525
|
+
puts option_parser
|
526
|
+
exit EXIT_CODES[:success]
|
527
|
+
end
|
528
|
+
command = "#{command}_#{args[0]}".to_sym
|
529
|
+
is_agent = true
|
530
|
+
end
|
531
|
+
|
293
532
|
unless COMMANDS.include?(command)
|
294
533
|
raise CommandError.new(:invalid_usage, "Unsupported command: #{command}")
|
295
534
|
end
|
@@ -298,18 +537,34 @@ NOTES
|
|
298
537
|
raise CommandError.new(:invalid_usage, "You must set Galaxy coordinator host by passing --coordinator COORDINATOR or by setting the GALAXY_COORDINATOR environment variable.")
|
299
538
|
end
|
300
539
|
|
301
|
-
return [command, filter, options, args.drop(1)]
|
540
|
+
return [command, filter, options, is_agent, args.drop(1)]
|
302
541
|
end
|
303
542
|
|
304
543
|
def self.execute(args)
|
305
544
|
begin
|
306
|
-
(command, filter, options, command_args) = parse_command_line(args)
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
545
|
+
(command, filter, options, is_agent, command_args) = parse_command_line(args)
|
546
|
+
result = Commands.send(command, filter, options, command_args)
|
547
|
+
if !is_agent then
|
548
|
+
slots = result.sort_by { |slot| [slot.ip, slot.binary || '', slot.config || '', slot.uuid] }
|
549
|
+
puts '' if options[:debug]
|
550
|
+
|
551
|
+
table = Table.new(['uuid', 'ip', 'status', 'binary', 'config', ''].map { |h| Colorize::colorize(h, :bright, :cyan) })
|
552
|
+
slots.each { |slot| table << slot.columns(STDOUT.tty?) }
|
553
|
+
puts table.render(STDOUT.tty?)
|
554
|
+
else
|
555
|
+
agents = result.sort_by { |agent| [agent.ip, agent.agent_id] }
|
556
|
+
puts '' if options[:debug]
|
557
|
+
|
558
|
+
table = Table.new(['id', 'ip', 'status', 'type', 'location'].map { |h| Colorize::colorize(h, :bright, :cyan) })
|
559
|
+
agents.each { |agent| table << agent.columns(STDOUT.tty?) }
|
560
|
+
puts table.render(STDOUT.tty?)
|
561
|
+
end
|
562
|
+
|
563
|
+
|
312
564
|
exit EXIT_CODES[:success]
|
565
|
+
rescue Errno::ECONNREFUSED
|
566
|
+
puts "Coordinator refused connection: #{options[:coordinator_url]}"
|
567
|
+
exit EXIT_CODES[:general_error]
|
313
568
|
rescue CommandError => e
|
314
569
|
puts e.message
|
315
570
|
if e.code == :invalid_usage
|
@@ -320,9 +575,15 @@ NOTES
|
|
320
575
|
puts ''
|
321
576
|
puts "exit: #{e.code}"
|
322
577
|
end
|
323
|
-
exit EXIT_CODES[e.code]
|
578
|
+
exit EXIT_CODES[e.code || :general_error]
|
324
579
|
end
|
325
580
|
end
|
326
581
|
|
327
582
|
end
|
328
583
|
end
|
584
|
+
|
585
|
+
|
586
|
+
if __FILE__ == $0 then
|
587
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
588
|
+
Galaxy::CLI.execute(ARGV)
|
589
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Colorize
|
2
|
+
module ANSIColors
|
3
|
+
COLORS = {
|
4
|
+
:normal => "\e[0m",
|
5
|
+
:bright => "\e[1m",
|
6
|
+
|
7
|
+
:red => "\e[31m",
|
8
|
+
:green => "\e[32m",
|
9
|
+
:blue => "\e[34m",
|
10
|
+
:cyan => "\e[36m"
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.colorize(string, *colors)
|
15
|
+
color_string = colors.map { |color| ANSIColors::COLORS[color] }.join
|
16
|
+
|
17
|
+
return "#{color_string}#{string}#{ANSIColors::COLORS[:normal]}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.strip_colors(string)
|
21
|
+
string.gsub(/\e\[\d+m/, '')
|
22
|
+
end
|
23
|
+
end
|
data/lib/galaxy/shell.rb
ADDED
data/lib/galaxy/table.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'galaxy/colorize'
|
2
|
+
|
3
|
+
class Table
|
4
|
+
def initialize(headers = nil, rows = nil)
|
5
|
+
@headers = headers
|
6
|
+
@rows = rows || []
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(row)
|
10
|
+
@rows << row
|
11
|
+
end
|
12
|
+
|
13
|
+
def calculate_widths
|
14
|
+
(@rows + [@headers]).
|
15
|
+
map { |cols| cols.map { |col| Colorize::strip_colors(col) }.map(&:size) }.
|
16
|
+
transpose.
|
17
|
+
slice(0..-2). # don't pad last column
|
18
|
+
map(&:max)
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(tty = true)
|
22
|
+
widths = calculate_widths
|
23
|
+
|
24
|
+
rows = @rows
|
25
|
+
rows = [@headers] + rows if tty
|
26
|
+
|
27
|
+
rows.map { |row| render_row(row, widths, tty) }.join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_row(row, col_widths, tty)
|
31
|
+
if tty
|
32
|
+
row.zip(col_widths).map do |value, width|
|
33
|
+
width ||= Colorize.strip_colors(value).length
|
34
|
+
value + ' ' * (width - Colorize.strip_colors(value).length)
|
35
|
+
end.join(' ')
|
36
|
+
else
|
37
|
+
row.map { |value| Colorize.strip_colors(value) }.join("\t")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
if __FILE__ == $0
|
44
|
+
table = Table.new(['uuid', 'ip', 'status', 'binary', 'config', ''])
|
45
|
+
|
46
|
+
table << [Colorize::colorize('1' * 6, :green), '2' * 5, '3' * 10, '4' * 10, '5' * 5, '6' * 3]
|
47
|
+
table << ['1' * 6, '2' * 5, '3' * 15, '4' * 7, '5' * 5, '6' * 10]
|
48
|
+
|
49
|
+
puts table.render(true)
|
50
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: com.proofpoint.galaxy.cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: "0.
|
5
|
+
version: "0.7"
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Dain Sundstrom
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2012-01-11 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -35,6 +35,17 @@ dependencies:
|
|
35
35
|
version: 1.5.1
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: sshkeyauth
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.0.11
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id003
|
38
49
|
description: Galaxy command line interface
|
39
50
|
email:
|
40
51
|
- dain@iq80.com
|
@@ -46,6 +57,9 @@ extra_rdoc_files: []
|
|
46
57
|
|
47
58
|
files:
|
48
59
|
- bin/galaxy
|
60
|
+
- lib/galaxy/colorize.rb
|
61
|
+
- lib/galaxy/shell.rb
|
62
|
+
- lib/galaxy/table.rb
|
49
63
|
- lib/galaxy/version.rb
|
50
64
|
- lib/galaxy.rb
|
51
65
|
has_rdoc: true
|