greenhat 0.1.4 → 0.3.1
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 +2 -7
- data/lib/greenhat/accessors/disk.rb +58 -2
- data/lib/greenhat/accessors/gitlab.rb +75 -0
- data/lib/greenhat/accessors/memory.rb +10 -10
- data/lib/greenhat/accessors/process.rb +10 -1
- data/lib/greenhat/cli.rb +128 -57
- data/lib/greenhat/color.rb +27 -0
- data/lib/greenhat/logbot.rb +9 -9
- data/lib/greenhat/settings.rb +51 -3
- data/lib/greenhat/shell/args.rb +146 -0
- data/lib/greenhat/shell/cat.rb +25 -73
- data/lib/greenhat/shell/color_string.rb +43 -0
- data/lib/greenhat/shell/disk.rb +30 -42
- data/lib/greenhat/shell/faststats.rb +80 -61
- data/lib/greenhat/shell/filter_help.rb +143 -0
- data/lib/greenhat/shell/gitlab.rb +61 -2
- data/lib/greenhat/shell/help.rb +98 -15
- data/lib/greenhat/shell/list.rb +46 -0
- data/lib/greenhat/shell/log.rb +78 -203
- data/lib/greenhat/shell/page.rb +39 -0
- data/lib/greenhat/shell/process.rb +57 -2
- data/lib/greenhat/shell/report.rb +70 -60
- data/lib/greenhat/shell/shell_helper.rb +601 -0
- data/lib/greenhat/shell.rb +27 -13
- data/lib/greenhat/thing/file_types.rb +76 -8
- data/lib/greenhat/thing/formatters/json_shellwords.rb +0 -3
- data/lib/greenhat/thing/formatters/nginx.rb +44 -0
- data/lib/greenhat/thing/formatters/syslog.rb +39 -0
- data/lib/greenhat/thing/helpers.rb +4 -4
- data/lib/greenhat/thing/kind.rb +9 -2
- data/lib/greenhat/thing/spinner.rb +3 -3
- data/lib/greenhat/thing.rb +3 -3
- data/lib/greenhat/tty/columns.rb +44 -0
- data/lib/greenhat/version.rb +1 -1
- data/lib/greenhat.rb +15 -14
- metadata +30 -20
- data/lib/greenhat/shell/helper.rb +0 -514
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# A module for calculating table data column widths
|
6
|
+
#
|
7
|
+
# Used by {Table} to manage column sizing.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module Columns
|
11
|
+
# Converts column widths to array format or infers default widths
|
12
|
+
#
|
13
|
+
# @param [TTY::Table] table
|
14
|
+
#
|
15
|
+
# @param [Array, Numeric, NilClass] column_widths
|
16
|
+
#
|
17
|
+
# @return [Array[Integer]]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def widths_from(table, column_widths = nil)
|
21
|
+
case column_widths
|
22
|
+
when Array
|
23
|
+
assert_widths(column_widths, table.columns_size)
|
24
|
+
Array(column_widths).map(&:to_i)
|
25
|
+
when Numeric
|
26
|
+
Array.new(table.columns_size, column_widths)
|
27
|
+
when NilClass
|
28
|
+
# THE SHIM! Strings that are too large fail to render correctly to do an empty result
|
29
|
+
# Set the maximum table width to half the screen size. Can't be full size due to the table headers
|
30
|
+
LogBot.debug('TTY Column Width') if ENV['DEBUG']
|
31
|
+
|
32
|
+
# TODO: Check Empty Results
|
33
|
+
return [0] if table.data.empty?
|
34
|
+
|
35
|
+
extract_widths(table.data).map { |x| x >= TTY::Screen.width ? (TTY::Screen.width * 3 / 4) : x }
|
36
|
+
|
37
|
+
else
|
38
|
+
raise TypeError, 'Invalid type for column widths'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
module_function :widths_from
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/greenhat/version.rb
CHANGED
data/lib/greenhat.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'action_view'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
require 'amazing_print'
|
4
7
|
require 'benchmark'
|
8
|
+
require 'dotenv'
|
5
9
|
require 'find'
|
6
|
-
require 'tty-prompt'
|
7
|
-
require 'tty-spinner'
|
8
|
-
require 'tty-table'
|
9
|
-
require 'tty-pager'
|
10
|
-
require 'tty-cursor'
|
11
|
-
require 'tty-reader'
|
12
10
|
require 'hash_dot'
|
13
11
|
require 'oj'
|
14
|
-
require '
|
15
|
-
require 'active_support'
|
16
|
-
require 'active_support/core_ext'
|
12
|
+
require 'pastel'
|
17
13
|
require 'pry'
|
18
|
-
require '
|
19
|
-
require 'colorize'
|
14
|
+
require 'require_all'
|
20
15
|
require 'semantic'
|
16
|
+
require 'slim'
|
17
|
+
require 'tty-cursor'
|
18
|
+
require 'tty-pager'
|
21
19
|
require 'tty-progressbar'
|
22
|
-
require '
|
20
|
+
require 'tty-prompt'
|
21
|
+
require 'tty-reader'
|
22
|
+
require 'tty-spinner'
|
23
|
+
require 'tty-table'
|
23
24
|
require 'warning'
|
24
|
-
require 'dotenv'
|
25
25
|
|
26
26
|
# Custom Gem
|
27
27
|
require 'teron'
|
@@ -49,6 +49,7 @@ require 'greenhat/archive'
|
|
49
49
|
require 'greenhat/host'
|
50
50
|
require 'greenhat/logbot'
|
51
51
|
require 'greenhat/settings'
|
52
|
+
require 'greenhat/color'
|
52
53
|
|
53
54
|
# Formatters - Loads Required Files Automatically
|
54
55
|
require 'greenhat/thing/super_log'
|
@@ -65,7 +66,6 @@ require 'greenhat/thing'
|
|
65
66
|
require 'greenhat/shell'
|
66
67
|
|
67
68
|
# TODO: Confirm
|
68
|
-
# require 'greenhat/thing/log_format'
|
69
69
|
# require 'greenhat/host'
|
70
70
|
# require 'greenhat/web'
|
71
71
|
|
@@ -76,5 +76,6 @@ require 'greenhat/pry_helpers'
|
|
76
76
|
require 'greenhat/tty/line'
|
77
77
|
require 'greenhat/tty/reader'
|
78
78
|
require 'greenhat/tty/custom_line'
|
79
|
+
require 'greenhat/tty/columns'
|
79
80
|
|
80
81
|
Warning.ignore(/The table size exceeds the currently set width/)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: greenhat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Davin Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amazing_print
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.14'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: minitest-reporters
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '1.4'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '1.4'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,20 +122,6 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '6.1'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: colorize
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0.8'
|
132
|
-
type: :runtime
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0.8'
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
126
|
name: did_you_mean
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -206,6 +192,20 @@ dependencies:
|
|
206
192
|
- - "~>"
|
207
193
|
- !ruby/object:Gem::Version
|
208
194
|
version: '0.8'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: pry
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0.14'
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0.14'
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
210
|
name: require_all
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -386,6 +386,7 @@ files:
|
|
386
386
|
- bin/greenhat
|
387
387
|
- lib/greenhat.rb
|
388
388
|
- lib/greenhat/accessors/disk.rb
|
389
|
+
- lib/greenhat/accessors/gitlab.rb
|
389
390
|
- lib/greenhat/accessors/logs/production.rb
|
390
391
|
- lib/greenhat/accessors/logs/sidekiq.rb
|
391
392
|
- lib/greenhat/accessors/memory.rb
|
@@ -393,22 +394,28 @@ files:
|
|
393
394
|
- lib/greenhat/accessors/process.rb
|
394
395
|
- lib/greenhat/archive.rb
|
395
396
|
- lib/greenhat/cli.rb
|
397
|
+
- lib/greenhat/color.rb
|
396
398
|
- lib/greenhat/host.rb
|
397
399
|
- lib/greenhat/logbot.rb
|
398
400
|
- lib/greenhat/pry_helpers.rb
|
399
401
|
- lib/greenhat/settings.rb
|
400
402
|
- lib/greenhat/shell.rb
|
403
|
+
- lib/greenhat/shell/args.rb
|
401
404
|
- lib/greenhat/shell/cat.rb
|
405
|
+
- lib/greenhat/shell/color_string.rb
|
402
406
|
- lib/greenhat/shell/disk.rb
|
403
407
|
- lib/greenhat/shell/faststats.rb
|
408
|
+
- lib/greenhat/shell/filter_help.rb
|
404
409
|
- lib/greenhat/shell/gitlab.rb
|
405
410
|
- lib/greenhat/shell/help.rb
|
406
|
-
- lib/greenhat/shell/
|
411
|
+
- lib/greenhat/shell/list.rb
|
407
412
|
- lib/greenhat/shell/log.rb
|
408
413
|
- lib/greenhat/shell/memory.rb
|
409
414
|
- lib/greenhat/shell/network.rb
|
415
|
+
- lib/greenhat/shell/page.rb
|
410
416
|
- lib/greenhat/shell/process.rb
|
411
417
|
- lib/greenhat/shell/report.rb
|
418
|
+
- lib/greenhat/shell/shell_helper.rb
|
412
419
|
- lib/greenhat/thing.rb
|
413
420
|
- lib/greenhat/thing/file_types.rb
|
414
421
|
- lib/greenhat/thing/formatters/api_json.rb
|
@@ -423,8 +430,10 @@ files:
|
|
423
430
|
- lib/greenhat/thing/formatters/json.rb
|
424
431
|
- lib/greenhat/thing/formatters/json_shellwords.rb
|
425
432
|
- lib/greenhat/thing/formatters/multiline_json.rb
|
433
|
+
- lib/greenhat/thing/formatters/nginx.rb
|
426
434
|
- lib/greenhat/thing/formatters/raw.rb
|
427
435
|
- lib/greenhat/thing/formatters/shellwords.rb
|
436
|
+
- lib/greenhat/thing/formatters/syslog.rb
|
428
437
|
- lib/greenhat/thing/formatters/table.rb
|
429
438
|
- lib/greenhat/thing/formatters/time_json.rb
|
430
439
|
- lib/greenhat/thing/formatters/time_shellwords.rb
|
@@ -435,6 +444,7 @@ files:
|
|
435
444
|
- lib/greenhat/thing/kind.rb
|
436
445
|
- lib/greenhat/thing/spinner.rb
|
437
446
|
- lib/greenhat/thing/super_log.rb
|
447
|
+
- lib/greenhat/tty/columns.rb
|
438
448
|
- lib/greenhat/tty/custom_line.rb
|
439
449
|
- lib/greenhat/tty/line.rb
|
440
450
|
- lib/greenhat/tty/reader.rb
|
@@ -1,514 +0,0 @@
|
|
1
|
-
module GreenHat
|
2
|
-
# Common Helpers
|
3
|
-
# rubocop:disable Metrics/ModuleLength
|
4
|
-
module ShellHelper
|
5
|
-
# Generic Parameter Parsing
|
6
|
-
def self.param_parse(params)
|
7
|
-
# Turn Params into Hash
|
8
|
-
opts = params.flat_map { |param| param_opt_scan(param) }
|
9
|
-
|
10
|
-
# Collect and Remove Args
|
11
|
-
args = params.each_with_object({}) { |param, obj| param_arg_scan(param, obj) }
|
12
|
-
|
13
|
-
# Move Special Arguments
|
14
|
-
opts.reject! do |opt|
|
15
|
-
if param_special_opts.include? opt.field
|
16
|
-
args[opt.field] = opt.value.split(',').map(&:to_sym)
|
17
|
-
true
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Remove Opts and Args
|
22
|
-
params.reject! do |param|
|
23
|
-
opt_field_remove?(opts, param) || arg_field_remove?(args, param)
|
24
|
-
end
|
25
|
-
|
26
|
-
[params, opts, args]
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.opt_field_remove?(opts, param)
|
30
|
-
opts.any? do |opt|
|
31
|
-
param.include? "--#{opt.field}#{opt.bang ? '!' : nil}=#{opt.value}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.arg_field_remove?(args, param)
|
36
|
-
args.keys.any? { |field| param.include? "--#{field}" }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Def Param Scan (Split -- values into keys)
|
40
|
-
def self.param_opt_scan(param)
|
41
|
-
param.scan(/--([^=]+)=(.*)/).map do |field, value|
|
42
|
-
bang = false
|
43
|
-
if field.include? '!'
|
44
|
-
field.delete!('!')
|
45
|
-
bang = true
|
46
|
-
end
|
47
|
-
|
48
|
-
{ field: field.to_sym, value: value, bang: bang }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Params that should be set as Args
|
53
|
-
# Comma Delimited
|
54
|
-
# --slice=thing,thing2
|
55
|
-
# --except=time,params
|
56
|
-
# --round=2
|
57
|
-
|
58
|
-
def self.param_special_opts
|
59
|
-
%i[slice except stats uniq pluck round archive start end sort]
|
60
|
-
end
|
61
|
-
|
62
|
-
# Parameter Extraction
|
63
|
-
# Special Opts: --slice, --except
|
64
|
-
def self.param_arg_scan(param, obj)
|
65
|
-
# TODO: Why is capture group doing two arrays
|
66
|
-
param.scan(/--([^=]+)$/).flatten.each do |field|
|
67
|
-
obj[field.to_sym] = param_arg_defaults(field.to_sym)
|
68
|
-
end
|
69
|
-
|
70
|
-
obj
|
71
|
-
end
|
72
|
-
|
73
|
-
# Arg Defaults
|
74
|
-
def self.param_arg_defaults(field)
|
75
|
-
case field
|
76
|
-
when *param_special_opts then []
|
77
|
-
when :round then 2
|
78
|
-
else
|
79
|
-
true
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Use File Process for Output
|
84
|
-
def self.file_output(files)
|
85
|
-
results = file_process(files) do |file|
|
86
|
-
[
|
87
|
-
file.friendly_name,
|
88
|
-
file.output(false),
|
89
|
-
"\n"
|
90
|
-
]
|
91
|
-
end
|
92
|
-
|
93
|
-
ShellHelper.show(results.flatten)
|
94
|
-
end
|
95
|
-
|
96
|
-
def self.file_process(files, &block)
|
97
|
-
files.map do |file|
|
98
|
-
next if file.output(false).empty?
|
99
|
-
|
100
|
-
block.call(file)
|
101
|
-
end.flatten
|
102
|
-
end
|
103
|
-
|
104
|
-
# Pagination Helper
|
105
|
-
def self.page(data)
|
106
|
-
TTY::Pager.page do |pager|
|
107
|
-
data.flatten.each do |output|
|
108
|
-
pager.write("\n#{output}") # write line to the pager
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Show Data / Auto Paginate Helper
|
114
|
-
def self.show(data, args = {})
|
115
|
-
# If Block of String
|
116
|
-
if data.instance_of?(String)
|
117
|
-
TTY::Pager.page data
|
118
|
-
return true
|
119
|
-
end
|
120
|
-
|
121
|
-
# If raw just print out
|
122
|
-
if args.raw
|
123
|
-
puts data.join("\n")
|
124
|
-
return true
|
125
|
-
end
|
126
|
-
|
127
|
-
# Check if content needs to paged, or if auto_height is off
|
128
|
-
if !args.page && (data.sum(&:size) < TTY::Screen.height)
|
129
|
-
puts data.map { |x| entry_show(x, args) }.compact.join("\n")
|
130
|
-
return true
|
131
|
-
end
|
132
|
-
|
133
|
-
# Default Pager
|
134
|
-
TTY::Pager.page do |pager|
|
135
|
-
data.each do |entry|
|
136
|
-
output = entry_show(entry, args)
|
137
|
-
|
138
|
-
next if output.blank?
|
139
|
-
|
140
|
-
pager.write("\n#{output}") # write line to the pager
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Entry Shower
|
146
|
-
def self.entry_show(entry, args)
|
147
|
-
case entry
|
148
|
-
when Hash then render_table(entry, args)
|
149
|
-
when String, Float, Integer then entry
|
150
|
-
when Array
|
151
|
-
entry.map { |x| x.ai(multiline: false) }.join("\n")
|
152
|
-
else
|
153
|
-
LogBot.warn('Shell Show', "Unknown #{entry.class}")
|
154
|
-
nil
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Print the Table in a Nice way
|
159
|
-
def self.render_table(entry, args)
|
160
|
-
table = TTY::Table.new(header: entry.keys, rows: [entry], orientation: :vertical)
|
161
|
-
|
162
|
-
table.render(:unicode, padding: [0, 1, 0, 1]) do |renderer|
|
163
|
-
renderer.border.style = :cyan
|
164
|
-
|
165
|
-
renderer.filter = lambda do |val, _row_index, col_index|
|
166
|
-
if col_index == 1
|
167
|
-
if val.numeric?
|
168
|
-
# TODO: Better Casting?
|
169
|
-
val = val.to_f.round(args.round.first.to_s.to_i) if args.round
|
170
|
-
val.to_s.colorize(:blue)
|
171
|
-
else
|
172
|
-
val.to_s
|
173
|
-
end
|
174
|
-
else
|
175
|
-
val.to_s
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Fall Back to Amazing Inspect
|
181
|
-
rescue StandardError => e
|
182
|
-
LogBot.warn('Table', message: e.message, backtrace: e.backtrace.first) if ENV['DEBUG']
|
183
|
-
|
184
|
-
[
|
185
|
-
entry.ai,
|
186
|
-
('_' * (TTY::Screen.width / 3)).colorize(:cyan),
|
187
|
-
"\n"
|
188
|
-
].join("\n")
|
189
|
-
end
|
190
|
-
|
191
|
-
# Main Entry Point for Filtering
|
192
|
-
def self.filter_start(log_list, filter_type, args, opts)
|
193
|
-
# Convert to Things
|
194
|
-
logs = ShellHelper.find_things(log_list, args).select(&:processed?)
|
195
|
-
|
196
|
-
# Ignore Archive/Host Dividers
|
197
|
-
if args[:combine]
|
198
|
-
results = logs.reject(&:blank?).map(&:data).flatten.compact
|
199
|
-
ShellHelper.filter(results, filter_type, args, opts)
|
200
|
-
else
|
201
|
-
# Iterate and Preserve Archive/Host Index
|
202
|
-
logs.each_with_object({}) do |log, obj|
|
203
|
-
# Ignore Empty Results / No Thing
|
204
|
-
next if log&.blank?
|
205
|
-
|
206
|
-
obj[log.friendly_name] = ShellHelper.filter(log.data, filter_type, args, opts)
|
207
|
-
|
208
|
-
obj
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
# Filter Logic
|
214
|
-
# TODO: Simplify
|
215
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
216
|
-
def self.filter(data, type = :all?, args = {}, opts = {})
|
217
|
-
results = data.clone.flatten.compact
|
218
|
-
results.select! do |row|
|
219
|
-
opts.send(type) do |opt|
|
220
|
-
filter_row_key(row, opt)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Time Filtering
|
225
|
-
results = filter_time(results, args) if args[:start] || args[:end]
|
226
|
-
|
227
|
-
# Strip Results if Slice is defined
|
228
|
-
results = filter_slice(results, args[:slice]) if args[:slice]
|
229
|
-
|
230
|
-
# Strip Results if Except is defined
|
231
|
-
results = filter_except(results, args[:except]) if args[:except]
|
232
|
-
|
233
|
-
# Remove Blank from either slice or except
|
234
|
-
results.reject!(&:empty?)
|
235
|
-
|
236
|
-
# Sort
|
237
|
-
results.sort_by! { |x| x.slice(*args[:sort]).values } if args[:sort]
|
238
|
-
|
239
|
-
# JSON Formatting
|
240
|
-
results = results.map { |x| Oj.dump(x) } if args[:json]
|
241
|
-
|
242
|
-
# Show Unique Only
|
243
|
-
results = filter_uniq(results, args[:uniq]) if args.key?(:uniq)
|
244
|
-
|
245
|
-
# Reverse
|
246
|
-
results.reverse! if args[:reverse]
|
247
|
-
|
248
|
-
# Count occurrences / Skip Results
|
249
|
-
return filter_stats(results, args[:stats]) if args[:stats]
|
250
|
-
|
251
|
-
# Pluck
|
252
|
-
results = filter_pluck(results, args[:pluck]) if args.key?(:pluck)
|
253
|
-
|
254
|
-
results
|
255
|
-
end
|
256
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
257
|
-
|
258
|
-
# Filter Start and End Times
|
259
|
-
# rubocop:disable Metrics/MethodLength
|
260
|
-
# TODO: This is a bit icky, simplify/dry
|
261
|
-
def self.filter_time(results, args)
|
262
|
-
if args.start
|
263
|
-
begin
|
264
|
-
time_start = Time.parse(args.start.first.to_s)
|
265
|
-
|
266
|
-
results.select! do |x|
|
267
|
-
if x.time
|
268
|
-
time_start < x.time
|
269
|
-
else
|
270
|
-
true
|
271
|
-
end
|
272
|
-
end
|
273
|
-
rescue StandardError
|
274
|
-
puts 'Unable to Process Start Time Filter'.colorize(:red)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
if args.end
|
279
|
-
begin
|
280
|
-
time_start = Time.parse(args.end.first.to_s)
|
281
|
-
|
282
|
-
results.select! do |x|
|
283
|
-
if x.time
|
284
|
-
time_start > x.time
|
285
|
-
else
|
286
|
-
true
|
287
|
-
end
|
288
|
-
end
|
289
|
-
rescue StandardError
|
290
|
-
puts 'Unable to Process End Time Filter'.colorize(:red)
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
results
|
295
|
-
end
|
296
|
-
# rubocop:enable Metrics/MethodLength
|
297
|
-
|
298
|
-
def self.filter_except(results, except)
|
299
|
-
# Avoid Empty Results
|
300
|
-
if slice.empty?
|
301
|
-
filter_empty_arg('slice')
|
302
|
-
return results
|
303
|
-
end
|
304
|
-
|
305
|
-
results.map { |row| row.except(*except) }
|
306
|
-
end
|
307
|
-
|
308
|
-
def self.filter_slice(results, slice)
|
309
|
-
# Avoid Empty Results
|
310
|
-
if slice.empty?
|
311
|
-
filter_empty_arg('slice')
|
312
|
-
return results
|
313
|
-
end
|
314
|
-
|
315
|
-
results.map { |row| row.slice(*slice) }
|
316
|
-
end
|
317
|
-
|
318
|
-
def self.filter_pluck(results, pluck)
|
319
|
-
# Avoid Empty Results
|
320
|
-
if pluck.empty?
|
321
|
-
filter_empty_arg('pluck')
|
322
|
-
return results
|
323
|
-
end
|
324
|
-
|
325
|
-
results.map { |x| x.slice(*pluck).values }.flatten
|
326
|
-
end
|
327
|
-
|
328
|
-
def self.filter_uniq(results, unique)
|
329
|
-
# Avoid Empty Results
|
330
|
-
if unique.empty?
|
331
|
-
filter_empty_arg('uniq')
|
332
|
-
return results
|
333
|
-
end
|
334
|
-
|
335
|
-
unique.map do |field|
|
336
|
-
results.uniq { |x| x[field] }
|
337
|
-
end.flatten
|
338
|
-
end
|
339
|
-
|
340
|
-
def self.filter_stats(results, stats)
|
341
|
-
# Avoid Empty Results
|
342
|
-
if stats.empty?
|
343
|
-
filter_empty_arg('stats')
|
344
|
-
return results
|
345
|
-
end
|
346
|
-
|
347
|
-
stats.map do |stat|
|
348
|
-
occurrences = filter_count_occurrences(results, stat)
|
349
|
-
occurrences.sort_by(&:last).to_h
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
# Helper to Count occurrences
|
354
|
-
def self.filter_count_occurrences(results, stat)
|
355
|
-
results.each_with_object(Hash.new(0)) do |entry, counts|
|
356
|
-
next unless entry.key? stat
|
357
|
-
|
358
|
-
counts[entry[stat]] += 1
|
359
|
-
|
360
|
-
counts
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
def self.filter_empty_arg(arg)
|
365
|
-
puts [
|
366
|
-
'Ignoring'.colorize(:light_yellow),
|
367
|
-
"--#{arg}".colorize(:cyan),
|
368
|
-
'it requires an argument'.colorize(:red)
|
369
|
-
].join(' ')
|
370
|
-
end
|
371
|
-
|
372
|
-
# Break out filter row logic into separate method
|
373
|
-
def self.filter_row_key(row, param)
|
374
|
-
# Ignore Other Logic if Field isn't even included
|
375
|
-
return false unless row.key? param.field
|
376
|
-
|
377
|
-
# Not Included Param
|
378
|
-
included = row[param.field].to_s.include? param.value.to_s
|
379
|
-
|
380
|
-
if param.bang
|
381
|
-
!included
|
382
|
-
else
|
383
|
-
included
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
# Total Count Helper
|
388
|
-
def self.total_count(results)
|
389
|
-
results.each do |k, v|
|
390
|
-
puts k
|
391
|
-
puts "Total: #{v.count.to_s.colorize(:blue)}"
|
392
|
-
puts
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
# Total Log List Manipulator
|
397
|
-
def self.prepare_list(log_list, base_list = nil)
|
398
|
-
base_list ||= GreenHat::ShellHelper::Log.list
|
399
|
-
|
400
|
-
# Assume all
|
401
|
-
log_list.push '*' if log_list.empty?
|
402
|
-
|
403
|
-
# Map for All
|
404
|
-
log_list = base_list.map(&:name) if log_list == ['*']
|
405
|
-
|
406
|
-
log_list
|
407
|
-
end
|
408
|
-
|
409
|
-
# Shortcut find things
|
410
|
-
def self.find_things(log_list, args = {})
|
411
|
-
things = log_list.uniq.flat_map do |log|
|
412
|
-
Thing.where name: log
|
413
|
-
end
|
414
|
-
|
415
|
-
# Host / Archive
|
416
|
-
things.select! { |x| x.archive? args.archive } if args.archive
|
417
|
-
|
418
|
-
things
|
419
|
-
end
|
420
|
-
|
421
|
-
# Main Entry Point for Searching
|
422
|
-
def self.search_start(log_list, filter_type, args, opts)
|
423
|
-
# Convert to Things
|
424
|
-
logs = ShellHelper.find_things(log_list, args)
|
425
|
-
|
426
|
-
logs.each_with_object({}) do |log, obj|
|
427
|
-
# Ignore Empty Results / No Thing
|
428
|
-
next if log&.data.blank?
|
429
|
-
|
430
|
-
obj[log.friendly_name] = ShellHelper.search(log.data, filter_type, args, opts)
|
431
|
-
|
432
|
-
obj
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
# Generic Search Helper / String/Regex
|
437
|
-
def self.search(data, type = :all?, args = {}, opts = {})
|
438
|
-
results = data.clone.flatten.compact
|
439
|
-
|
440
|
-
results.select! do |row|
|
441
|
-
opts.send(type) do |opt|
|
442
|
-
search_row(row, opt)
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
# Strip Results if Slice is defined
|
447
|
-
results.map! { |row| row.slice(*args[:slice]) } if args[:slice]
|
448
|
-
|
449
|
-
# Strip Results if Except is defined
|
450
|
-
results.map! { |row| row.except(*args[:except]) } if args[:except]
|
451
|
-
|
452
|
-
# Remove Blank from either slice or except
|
453
|
-
results.reject!(&:empty?)
|
454
|
-
|
455
|
-
results
|
456
|
-
end
|
457
|
-
|
458
|
-
# Break out filter row logic into separate method
|
459
|
-
def self.search_row(row, param)
|
460
|
-
# Not Included Param
|
461
|
-
included = row.to_s.include? param.value
|
462
|
-
|
463
|
-
if param.bang
|
464
|
-
!included
|
465
|
-
else
|
466
|
-
included
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
# Color Reader Helper
|
471
|
-
def self.pastel
|
472
|
-
@pastel ||= Pastel.new
|
473
|
-
end
|
474
|
-
|
475
|
-
# Number Helper
|
476
|
-
# https://gitlab.com/zedtux/human_size_to_number/-/blob/master/lib/human_size_to_number/helper.rb
|
477
|
-
def self.human_size_to_number(string)
|
478
|
-
size, unit = string.scan(/(\d*\.?\d+)\s?(Bytes?|KB|MB|GB|TB)/i).first
|
479
|
-
number = size.to_f
|
480
|
-
|
481
|
-
number = case unit.downcase
|
482
|
-
when 'byte', 'bytes'
|
483
|
-
number
|
484
|
-
when 'kb'
|
485
|
-
number * 1024
|
486
|
-
when 'mb'
|
487
|
-
number * 1024 * 1024
|
488
|
-
when 'gb'
|
489
|
-
number * 1024 * 1024 * 1024
|
490
|
-
when 'tb'
|
491
|
-
number * 1024 * 1024 * 1024 * 1024
|
492
|
-
end
|
493
|
-
number.round
|
494
|
-
end
|
495
|
-
|
496
|
-
# TODO: Needed?
|
497
|
-
def self.filter_and(data, params = {})
|
498
|
-
result = data.clone.flatten.compact
|
499
|
-
params.each do |k, v|
|
500
|
-
result.select! do |row|
|
501
|
-
if row.key? k.to_sym
|
502
|
-
row[k.to_sym].include? v
|
503
|
-
else
|
504
|
-
false
|
505
|
-
end
|
506
|
-
end
|
507
|
-
next
|
508
|
-
end
|
509
|
-
|
510
|
-
result
|
511
|
-
end
|
512
|
-
end
|
513
|
-
# rubocop:enable Metrics/ModuleLength
|
514
|
-
end
|