greenhat 0.1.4
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/README.md +64 -0
- data/bin/greenhat +12 -0
- data/lib/greenhat.rb +80 -0
- data/lib/greenhat/accessors/disk.rb +27 -0
- data/lib/greenhat/accessors/logs/production.rb +41 -0
- data/lib/greenhat/accessors/logs/sidekiq.rb +41 -0
- data/lib/greenhat/accessors/memory.rb +46 -0
- data/lib/greenhat/accessors/network.rb +8 -0
- data/lib/greenhat/accessors/process.rb +8 -0
- data/lib/greenhat/archive.rb +108 -0
- data/lib/greenhat/cli.rb +448 -0
- data/lib/greenhat/host.rb +182 -0
- data/lib/greenhat/logbot.rb +86 -0
- data/lib/greenhat/pry_helpers.rb +51 -0
- data/lib/greenhat/settings.rb +51 -0
- data/lib/greenhat/shell.rb +92 -0
- data/lib/greenhat/shell/cat.rb +125 -0
- data/lib/greenhat/shell/disk.rb +68 -0
- data/lib/greenhat/shell/faststats.rb +195 -0
- data/lib/greenhat/shell/gitlab.rb +45 -0
- data/lib/greenhat/shell/help.rb +15 -0
- data/lib/greenhat/shell/helper.rb +514 -0
- data/lib/greenhat/shell/log.rb +344 -0
- data/lib/greenhat/shell/memory.rb +31 -0
- data/lib/greenhat/shell/network.rb +12 -0
- data/lib/greenhat/shell/process.rb +12 -0
- data/lib/greenhat/shell/report.rb +319 -0
- data/lib/greenhat/thing.rb +121 -0
- data/lib/greenhat/thing/file_types.rb +705 -0
- data/lib/greenhat/thing/formatters/api_json.rb +34 -0
- data/lib/greenhat/thing/formatters/bracket_log.rb +44 -0
- data/lib/greenhat/thing/formatters/clean_raw.rb +23 -0
- data/lib/greenhat/thing/formatters/colon_split_strip.rb +12 -0
- data/lib/greenhat/thing/formatters/dotenv.rb +10 -0
- data/lib/greenhat/thing/formatters/format.rb +12 -0
- data/lib/greenhat/thing/formatters/free_m.rb +29 -0
- data/lib/greenhat/thing/formatters/gitlab_ctl_tail.rb +51 -0
- data/lib/greenhat/thing/formatters/gitlab_status.rb +26 -0
- data/lib/greenhat/thing/formatters/json.rb +63 -0
- data/lib/greenhat/thing/formatters/json_shellwords.rb +44 -0
- data/lib/greenhat/thing/formatters/multiline_json.rb +10 -0
- data/lib/greenhat/thing/formatters/raw.rb +18 -0
- data/lib/greenhat/thing/formatters/shellwords.rb +23 -0
- data/lib/greenhat/thing/formatters/table.rb +26 -0
- data/lib/greenhat/thing/formatters/time_json.rb +21 -0
- data/lib/greenhat/thing/formatters/time_shellwords.rb +28 -0
- data/lib/greenhat/thing/formatters/time_space.rb +36 -0
- data/lib/greenhat/thing/helpers.rb +71 -0
- data/lib/greenhat/thing/history.rb +51 -0
- data/lib/greenhat/thing/info_format.rb +193 -0
- data/lib/greenhat/thing/kind.rb +97 -0
- data/lib/greenhat/thing/spinner.rb +52 -0
- data/lib/greenhat/thing/super_log.rb +102 -0
- data/lib/greenhat/tty/custom_line.rb +29 -0
- data/lib/greenhat/tty/line.rb +326 -0
- data/lib/greenhat/tty/reader.rb +110 -0
- data/lib/greenhat/version.rb +3 -0
- data/lib/greenhat/views/css.slim +126 -0
- data/lib/greenhat/views/disk_free.slim +18 -0
- data/lib/greenhat/views/index.slim +51 -0
- data/lib/greenhat/views/info.slim +39 -0
- data/lib/greenhat/views/manifest.slim +22 -0
- data/lib/greenhat/views/memory.slim +18 -0
- data/lib/greenhat/views/netstat.slim +20 -0
- data/lib/greenhat/views/process.slim +21 -0
- data/lib/greenhat/views/systemctl.slim +40 -0
- data/lib/greenhat/views/ulimit.slim +15 -0
- data/lib/greenhat/web.rb +46 -0
- metadata +476 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module GreenHat
|
2
|
+
# Info Formatter
|
3
|
+
module Spinner
|
4
|
+
def spin_start(title)
|
5
|
+
# Ignore when Quiet is Set
|
6
|
+
return true if Cli.quiet
|
7
|
+
|
8
|
+
@spin_time = Time.now
|
9
|
+
@spinner = TTY::Spinner.new(
|
10
|
+
"#{time.colorize(:light_black)} - [:spinner] :title", hide_cursor: true, success_mark: '✔'.colorize(:green)
|
11
|
+
)
|
12
|
+
@spinner.update(title: title)
|
13
|
+
|
14
|
+
# Don't Auto spin when debug output is happening
|
15
|
+
@spinner.auto_spin unless ENV['DEBUG']
|
16
|
+
end
|
17
|
+
|
18
|
+
def spin_done
|
19
|
+
# Ignore when Quiet is Set
|
20
|
+
return true if Cli.quiet
|
21
|
+
|
22
|
+
title = @spinner.tokens[:title]
|
23
|
+
spin_end = humanize(@spin_time)
|
24
|
+
|
25
|
+
title_update = if spin_end.blank?
|
26
|
+
title
|
27
|
+
else
|
28
|
+
"#{title} (#{spin_end.colorize(:blue)})"
|
29
|
+
end
|
30
|
+
|
31
|
+
@spinner.update(title: title_update)
|
32
|
+
@spinner.success
|
33
|
+
end
|
34
|
+
|
35
|
+
def time
|
36
|
+
Time.now.strftime('%I:%M:%S').colorize(:light_black)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Replace TimeDifference with https://stackoverflow.com/a/4136485/1678507
|
40
|
+
def humanize(time)
|
41
|
+
miliseconds = (Time.now - time) * 1000
|
42
|
+
|
43
|
+
[[1000, :ms], [60, :s], [60, :m], [24, :h]].map do |count, name|
|
44
|
+
next unless miliseconds.positive?
|
45
|
+
|
46
|
+
miliseconds, n = miliseconds.divmod(count)
|
47
|
+
|
48
|
+
"#{n.to_i}#{name}" unless n.to_i.zero?
|
49
|
+
end.compact.reverse.join(' ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# TODO: Deprecate, was used specifically for Elk
|
2
|
+
# rubocop:disable /AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/
|
3
|
+
module GreenHat
|
4
|
+
# Log Identifier
|
5
|
+
module SuperLog
|
6
|
+
def self.log?(kind)
|
7
|
+
[
|
8
|
+
'gitaly/current',
|
9
|
+
'gitaly/gitaly_ruby_json.log',
|
10
|
+
'gitlab-rails/api_json.log',
|
11
|
+
'gitlab-rails/application_json.log',
|
12
|
+
'gitlab-rails/audit_json.log',
|
13
|
+
'gitlab-rails/graphql_json.log',
|
14
|
+
'gitlab-rails/integrations_json.log',
|
15
|
+
'gitlab-rails/production_json.log',
|
16
|
+
'gitlab-rails/sidekiq_client.log',
|
17
|
+
'gitlab-shell/gitlab-shell.log',
|
18
|
+
'gitlab-workhorse/current',
|
19
|
+
'puma/puma_stdout.log',
|
20
|
+
'sidekiq/current',
|
21
|
+
'gitlab-rails/importer.log',
|
22
|
+
'gitlabsos.log',
|
23
|
+
'nginx/gitlab_access.log',
|
24
|
+
'patroni/current'
|
25
|
+
].any? { |x| x.include? kind.to_s }
|
26
|
+
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# ==========================================================================
|
31
|
+
# File Pattern Matching
|
32
|
+
# ==========================================================================
|
33
|
+
def self.api_json_format?(path)
|
34
|
+
['rails_api_json_log'].any? { |x| path.include? x }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.unicorn_stderr_format?(path)
|
38
|
+
%w[
|
39
|
+
unicorn_stderr
|
40
|
+
].any? { |x| path.include? x }
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.reconfigure_format?(path)
|
44
|
+
%w[
|
45
|
+
reconfigure
|
46
|
+
].any? { |x| path.include? x }
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.shellwords?(path)
|
50
|
+
%w[
|
51
|
+
gitlab_pages_current
|
52
|
+
alertmanager_current
|
53
|
+
registry_current
|
54
|
+
prometheus_current
|
55
|
+
|
56
|
+
].any? { |x| path.include? x }
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.time_space?(path)
|
60
|
+
%w[
|
61
|
+
postgresql_current
|
62
|
+
redis_current
|
63
|
+
unicorn_current
|
64
|
+
gitlab_monitor_current
|
65
|
+
sidekiq_exporter_log
|
66
|
+
].any? { |x| path.include? x }
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.json_format?(path)
|
70
|
+
%w[
|
71
|
+
production_json_log
|
72
|
+
gitaly_current
|
73
|
+
geo_log
|
74
|
+
sidekiq_current
|
75
|
+
sidekiq_log
|
76
|
+
gitlab_shell_gitlab_shell_log
|
77
|
+
gitlab_rails_audit_json_log
|
78
|
+
gitlab_rails_application_json_log
|
79
|
+
].any? { |x| path.include? x }
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.dmesg_format?(path)
|
83
|
+
%w[
|
84
|
+
dmesg
|
85
|
+
].any? { |x| path.include? x }
|
86
|
+
end
|
87
|
+
# ==========================================================================
|
88
|
+
|
89
|
+
# Identify Formatter
|
90
|
+
def self.type?(path)
|
91
|
+
return :api_json_format if api_json_format?(path)
|
92
|
+
return :unicorn_stderr_format if unicorn_stderr_format?(path)
|
93
|
+
return :reconfigure_format if reconfigure_format?(path)
|
94
|
+
return :shellwords if shellwords?(path)
|
95
|
+
return :time_space if time_space?(path)
|
96
|
+
return :json_format if json_format?(path)
|
97
|
+
return :dmesg_format if dmesg_format?(path)
|
98
|
+
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TTY
|
2
|
+
class Reader
|
3
|
+
# Shim Helper
|
4
|
+
class Line
|
5
|
+
def move_word_left
|
6
|
+
loop do
|
7
|
+
# Don't go past beginning
|
8
|
+
break if @cursor.zero?
|
9
|
+
|
10
|
+
left 1
|
11
|
+
|
12
|
+
break if self[@cursor].blank?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def move_word_right
|
17
|
+
loop do
|
18
|
+
right 1
|
19
|
+
break if self[@cursor].blank?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# No Tabs
|
24
|
+
def insert(chars)
|
25
|
+
self[@cursor] = chars unless ["\t", "\e[Z"].include?(chars)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,326 @@
|
|
1
|
+
# https://github.com/piotrmurach/tty-reader/commit/cc38eadf888ac179a77d72c21ebda987cfea3aca
|
2
|
+
|
3
|
+
# Disabling Since this is a Shim
|
4
|
+
# rubocop:disable all
|
5
|
+
|
6
|
+
|
7
|
+
module TTY
|
8
|
+
class Reader
|
9
|
+
class Line
|
10
|
+
# ANSI_MATCHER = /(\[)?\033(\[)?[;?\d]*[\dA-Za-z](\])?/
|
11
|
+
|
12
|
+
# The word break characters list used by shell
|
13
|
+
DEFAULT_WORD_BREAK_CHARACTERS = " \t\n\"\\'`@$><=|&{("
|
14
|
+
|
15
|
+
# Strip ANSI characters from the text
|
16
|
+
#
|
17
|
+
# @param [String] text
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def self.sanitize(text)
|
23
|
+
text.dup.gsub(ANSI_MATCHER, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
# The editable text
|
27
|
+
# @api public
|
28
|
+
attr_accessor :text
|
29
|
+
|
30
|
+
# The current cursor position witin the text
|
31
|
+
# @api public
|
32
|
+
attr_reader :cursor
|
33
|
+
|
34
|
+
# The line mode
|
35
|
+
# @api public
|
36
|
+
attr_reader :mode
|
37
|
+
|
38
|
+
# The prompt displayed before input
|
39
|
+
# @api public
|
40
|
+
attr_reader :prompt
|
41
|
+
|
42
|
+
# The word separator pattern for splitting the text
|
43
|
+
#
|
44
|
+
# @return [Regexp]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
attr_reader :separator
|
48
|
+
|
49
|
+
# Create a Line instance
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def initialize(text = '', prompt: '', separator: nil)
|
53
|
+
@text = text.dup
|
54
|
+
@prompt = prompt.dup
|
55
|
+
break_chars = DEFAULT_WORD_BREAK_CHARACTERS.chars
|
56
|
+
@separator = separator || Regexp.union(break_chars)
|
57
|
+
@cursor = [0, @text.length].max
|
58
|
+
@mode = :edit
|
59
|
+
|
60
|
+
yield self if block_given?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Check if line is in edit mode
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
#
|
67
|
+
# @public
|
68
|
+
def editing?
|
69
|
+
@mode == :edit
|
70
|
+
end
|
71
|
+
|
72
|
+
# Enable edit mode
|
73
|
+
#
|
74
|
+
# @return [Boolean]
|
75
|
+
#
|
76
|
+
# @public
|
77
|
+
def edit_mode
|
78
|
+
@mode = :edit
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check if line is in replace mode
|
82
|
+
#
|
83
|
+
# @return [Boolean]
|
84
|
+
#
|
85
|
+
# @public
|
86
|
+
def replacing?
|
87
|
+
@mode == :replace
|
88
|
+
end
|
89
|
+
|
90
|
+
# Enable replace mode
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
#
|
94
|
+
# @public
|
95
|
+
def replace_mode
|
96
|
+
@mode = :replace
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check if cursor reached beginning of the line
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
def start?
|
105
|
+
@cursor.zero?
|
106
|
+
end
|
107
|
+
|
108
|
+
# Check if cursor reached end of the line
|
109
|
+
#
|
110
|
+
# @return [Boolean]
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
def end?
|
114
|
+
@cursor == @text.length
|
115
|
+
end
|
116
|
+
|
117
|
+
# Move line position to the left by n chars
|
118
|
+
#
|
119
|
+
# @api public
|
120
|
+
def left(n = 1)
|
121
|
+
@cursor = [0, @cursor - n].max
|
122
|
+
end
|
123
|
+
|
124
|
+
# Move line position to the right by n chars
|
125
|
+
#
|
126
|
+
# @api public
|
127
|
+
def right(n = 1)
|
128
|
+
@cursor = [@text.length, @cursor + n].min
|
129
|
+
end
|
130
|
+
|
131
|
+
# Move cursor to beginning position
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
def move_to_start
|
135
|
+
@cursor = 0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Move cursor to end position
|
139
|
+
#
|
140
|
+
# @api public
|
141
|
+
def move_to_end
|
142
|
+
@cursor = @text.length # put cursor outside of text
|
143
|
+
end
|
144
|
+
|
145
|
+
# Insert characters inside a line. When the lines exceeds
|
146
|
+
# maximum length, an extra space is added to accomodate index.
|
147
|
+
#
|
148
|
+
# @param [Integer] i
|
149
|
+
# the index to insert at
|
150
|
+
#
|
151
|
+
# @param [String] chars
|
152
|
+
# the characters to insert
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# text = "aaa"
|
156
|
+
# line[5]= "b"
|
157
|
+
# => "aaa b"
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
def []=(i, chars)
|
161
|
+
edit_mode
|
162
|
+
|
163
|
+
if i.is_a?(Range)
|
164
|
+
@text[i] = chars
|
165
|
+
@cursor += chars.length
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
if i <= 0
|
170
|
+
before_text = ''
|
171
|
+
after_text = @text.dup
|
172
|
+
elsif i > @text.length - 1 # insert outside of line input
|
173
|
+
before_text = @text.dup
|
174
|
+
after_text = "\s" * (i - @text.length)
|
175
|
+
@cursor += after_text.length
|
176
|
+
else
|
177
|
+
before_text = @text[0..i - 1].dup
|
178
|
+
after_text = @text[i..-1].dup
|
179
|
+
end
|
180
|
+
|
181
|
+
@text = if i > @text.length - 1
|
182
|
+
before_text + after_text + chars
|
183
|
+
else
|
184
|
+
before_text + chars + after_text
|
185
|
+
end
|
186
|
+
|
187
|
+
@cursor = i + chars.length
|
188
|
+
end
|
189
|
+
|
190
|
+
# Read character
|
191
|
+
#
|
192
|
+
# @api public
|
193
|
+
def [](i)
|
194
|
+
@text[i]
|
195
|
+
end
|
196
|
+
|
197
|
+
# Find a word under the cursor based on the word separator
|
198
|
+
#
|
199
|
+
# @param [Boolean] before
|
200
|
+
# whether to start searching before or after a break character
|
201
|
+
#
|
202
|
+
# @return [String]
|
203
|
+
#
|
204
|
+
# @api public
|
205
|
+
def word(before: true)
|
206
|
+
@text[range(before: before)]
|
207
|
+
end
|
208
|
+
|
209
|
+
# Find a range of characters under the cursor based on the word separator
|
210
|
+
#
|
211
|
+
# @param [Integer] from
|
212
|
+
# the start index
|
213
|
+
#
|
214
|
+
# @param [Symbol] before
|
215
|
+
# whether to start search before or after break character
|
216
|
+
#
|
217
|
+
# @return [Range]
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
def range(from: @cursor, before: true)
|
221
|
+
# move back or forward by one character when at a word boundary
|
222
|
+
if word_boundary?
|
223
|
+
from = before ? from - 1 : from + 1
|
224
|
+
end
|
225
|
+
|
226
|
+
# find start position
|
227
|
+
start_pos = @text.rindex(separator, from) || 0
|
228
|
+
start_pos += 1 unless start_pos.zero?
|
229
|
+
|
230
|
+
# find end position
|
231
|
+
end_pos = @text.index(separator, start_pos) || text_size
|
232
|
+
end_pos -= 1 unless @text.empty?
|
233
|
+
|
234
|
+
start_pos..end_pos
|
235
|
+
end
|
236
|
+
|
237
|
+
# Check if cursor is at a word boundary
|
238
|
+
#
|
239
|
+
# @return [Boolean]
|
240
|
+
#
|
241
|
+
# @api private
|
242
|
+
def word_boundary?
|
243
|
+
@text[@cursor] =~ separator
|
244
|
+
end
|
245
|
+
|
246
|
+
# Replace current line with new text
|
247
|
+
#
|
248
|
+
# @param [String] text
|
249
|
+
#
|
250
|
+
# @api public
|
251
|
+
def replace(text)
|
252
|
+
@text = text
|
253
|
+
@cursor = @text.length # put cursor outside of text
|
254
|
+
replace_mode
|
255
|
+
end
|
256
|
+
|
257
|
+
# Insert char(s) at cursor position
|
258
|
+
#
|
259
|
+
# @api public
|
260
|
+
def insert(chars)
|
261
|
+
self[@cursor] = chars
|
262
|
+
end
|
263
|
+
|
264
|
+
# Add char and move cursor
|
265
|
+
#
|
266
|
+
# @api public
|
267
|
+
def <<(char)
|
268
|
+
@text << char
|
269
|
+
@cursor += 1
|
270
|
+
end
|
271
|
+
|
272
|
+
# Remove char from the line at current position
|
273
|
+
#
|
274
|
+
# @api public
|
275
|
+
def delete(n = 1)
|
276
|
+
@text.slice!(@cursor, n)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Remove char from the line in front of the cursor
|
280
|
+
#
|
281
|
+
# @param [Integer] n
|
282
|
+
# the number of chars to remove
|
283
|
+
#
|
284
|
+
# @api public
|
285
|
+
def remove(n = 1)
|
286
|
+
left(n)
|
287
|
+
@text.slice!(@cursor, n)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Full line with prompt as string
|
291
|
+
#
|
292
|
+
# @api public
|
293
|
+
def to_s
|
294
|
+
"#{@prompt}#{@text}"
|
295
|
+
end
|
296
|
+
alias inspect to_s
|
297
|
+
|
298
|
+
# Prompt size
|
299
|
+
#
|
300
|
+
# @api public
|
301
|
+
def prompt_size
|
302
|
+
p = self.class.sanitize(@prompt).split(/\r?\n/)
|
303
|
+
# return the length of each line + screen width for every line past the first
|
304
|
+
# which accounts for multi-line prompts
|
305
|
+
p.join.length + ((p.length - 1) * TTY::Screen.width)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Text size
|
309
|
+
#
|
310
|
+
# @api public
|
311
|
+
def text_size
|
312
|
+
self.class.sanitize(@text).size
|
313
|
+
end
|
314
|
+
|
315
|
+
# Full line size with prompt
|
316
|
+
#
|
317
|
+
# @api public
|
318
|
+
def size
|
319
|
+
prompt_size + text_size
|
320
|
+
end
|
321
|
+
alias length size
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# rubocop:enable all
|