greenhat 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/greenhat/accessors/disk.rb +41 -0
- data/lib/greenhat/accessors/gitlab.rb +26 -0
- data/lib/greenhat/archive.rb +4 -0
- data/lib/greenhat/cli.rb +1 -0
- data/lib/greenhat/shell/filter_help.rb +203 -2
- data/lib/greenhat/shell/log.rb +11 -2
- data/lib/greenhat/shell/markdown.rb +356 -0
- data/lib/greenhat/shell/report.rb +34 -3
- data/lib/greenhat/shell/shell_helper.rb +12 -2
- data/lib/greenhat/thing/file_types.rb +92 -7
- data/lib/greenhat/thing/formatters/kube_json.rb +36 -0
- data/lib/greenhat/thing/formatters/kube_nginx.rb +48 -0
- data/lib/greenhat/thing/formatters/kube_webservice.rb +51 -0
- data/lib/greenhat/thing/formatters/registry.rb +47 -0
- data/lib/greenhat/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c055abc7397135be9ca07e7965fc88d67f1d7d16099a5cc5db1977986490361
|
4
|
+
data.tar.gz: 2e2f66a3bbc5bdc84e8b5bef9abfa28706f48841f25c7e642fe352c063745e22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9fcd1274e4887fa332a053185ea1049e5db2f3d449714542a31084234326d48623e95e0fb8678b6cb67b20b636ec04c82db1555ff43aac09f158d6df4fc7dc0
|
7
|
+
data.tar.gz: c6fe018479b7cc90f2e70b411761fe5e3b8da5d4237064e3bb450399f966cf00a0302adc433ebf39fad57565ca11efbd95fbd0601a15f2989bc1c6455029a4e8
|
@@ -79,5 +79,46 @@ module GreenHat
|
|
79
79
|
output
|
80
80
|
end
|
81
81
|
# rubocop:enable Metrics/MethodLength
|
82
|
+
|
83
|
+
# Unified Output Handler
|
84
|
+
def self.markdown_format(file, name = false, limit = nil, filter = %w[tmpfs loop])
|
85
|
+
output = []
|
86
|
+
|
87
|
+
output << file.friendly_name if name
|
88
|
+
|
89
|
+
# Reject TMPFS
|
90
|
+
disks = file.data.sort_by { |x| x.use.to_i }.reverse
|
91
|
+
|
92
|
+
# Filter
|
93
|
+
disks.reject! { |x| filter.any? { |y| x.filesystem.include? y } }
|
94
|
+
|
95
|
+
disks = disks[0..limit - 1] if limit
|
96
|
+
|
97
|
+
pad_mount, pad_size, pad_used, pad_avail = GreenHat::Disk.padding(disks)
|
98
|
+
|
99
|
+
# Headers
|
100
|
+
output << [
|
101
|
+
'Mount'.ljust(pad_mount),
|
102
|
+
'Size'.ljust(pad_size),
|
103
|
+
'Used'.ljust(pad_used),
|
104
|
+
'Avail'.ljust(pad_avail),
|
105
|
+
'% Use'.ljust(pad_avail)
|
106
|
+
].join
|
107
|
+
|
108
|
+
# Table Summary
|
109
|
+
disks.map do |disk|
|
110
|
+
# Whole Thing
|
111
|
+
output << [
|
112
|
+
disk.mounted_on.ljust(pad_mount),
|
113
|
+
disk[:size].to_s.ljust(pad_size),
|
114
|
+
disk.used.to_s.ljust(pad_used),
|
115
|
+
disk.avail.to_s.ljust(pad_avail),
|
116
|
+
disk.use.rjust(5).ljust(5)
|
117
|
+
].join
|
118
|
+
end
|
119
|
+
|
120
|
+
output
|
121
|
+
end
|
122
|
+
# =
|
82
123
|
end
|
83
124
|
end
|
@@ -71,5 +71,31 @@ module GreenHat
|
|
71
71
|
|
72
72
|
table.render(:unicode, padding: [0, 1, 0, 1], indent: indent)
|
73
73
|
end
|
74
|
+
|
75
|
+
def self.services_markdown(archive)
|
76
|
+
# manifest = archive.things.find { |x| x.type == 'gitlab/version-manifest.json' }
|
77
|
+
gitlab_status = archive.things.find { |x| x.name == 'gitlab_status' }
|
78
|
+
|
79
|
+
list = gitlab_status.data.keys.sort.map do |service|
|
80
|
+
status = gitlab_status.data.dig(service, 0, :status) == 'run' ? '↑' : '!'
|
81
|
+
|
82
|
+
"#{service}#{status}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Keep Alphabetical Sort
|
86
|
+
groups = list.each_slice((list.size / 3.to_f).round).to_a
|
87
|
+
|
88
|
+
table = TTY::Table.new do |t|
|
89
|
+
loop do
|
90
|
+
break if groups.all?(&:empty?)
|
91
|
+
|
92
|
+
t << groups.map(&:shift)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
"```\n#{table.render(:basic)}\n```"
|
97
|
+
end
|
98
|
+
|
99
|
+
# ==
|
74
100
|
end
|
75
101
|
end
|
data/lib/greenhat/archive.rb
CHANGED
data/lib/greenhat/cli.rb
CHANGED
@@ -2,8 +2,8 @@ module GreenHat
|
|
2
2
|
# CLI Helper
|
3
3
|
module ShellHelper
|
4
4
|
# Unify Filter / Filter Help
|
5
|
+
# rubocop:disable Metrics/MethodLength,Metrics/ModuleLength
|
5
6
|
module Filter
|
6
|
-
# rubocop:disable Metrics/MethodLength
|
7
7
|
def self.help
|
8
8
|
puts "\u2500".pastel(:cyan) * 20
|
9
9
|
puts 'Filter'.pastel(:yellow)
|
@@ -156,7 +156,208 @@ module GreenHat
|
|
156
156
|
|
157
157
|
puts
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
|
+
def self.help_index
|
161
|
+
{
|
162
|
+
title: [
|
163
|
+
"\u2500".pastel(:cyan) * 20,
|
164
|
+
'Filter'.pastel(:yellow),
|
165
|
+
"\u2500".pastel(:cyan) * 20
|
166
|
+
],
|
167
|
+
options: [
|
168
|
+
'Options'.pastel(:blue)
|
169
|
+
],
|
170
|
+
|
171
|
+
raw: [
|
172
|
+
'--raw'.pastel(:green),
|
173
|
+
' Disable formatting and page/less'
|
174
|
+
],
|
175
|
+
|
176
|
+
page: [
|
177
|
+
'--page'.pastel(:green),
|
178
|
+
' Specifically enable or disable paging',
|
179
|
+
' E.g. --page (default to true), --page=true, --page=false'
|
180
|
+
],
|
181
|
+
|
182
|
+
round: ['--round'.pastel(:green),
|
183
|
+
' Attempt to round all integers. Default: 2.',
|
184
|
+
' E.g. --round, --round=3, --round=0'],
|
185
|
+
|
186
|
+
limit: [
|
187
|
+
|
188
|
+
'--limit'.pastel(:green),
|
189
|
+
' Limit total output lines. Disabled by default. Default without value is based on screen height',
|
190
|
+
' E.g. --limit, --limit=5'
|
191
|
+
],
|
192
|
+
|
193
|
+
json: [
|
194
|
+
'--json'.pastel(:green),
|
195
|
+
' Print output back into JSON'
|
196
|
+
],
|
197
|
+
|
198
|
+
or: [
|
199
|
+
'--or'.pastel(:green),
|
200
|
+
' Filters will use OR instead of AND (all match vs any match)'
|
201
|
+
],
|
202
|
+
|
203
|
+
total: [
|
204
|
+
'--total'.pastel(:green),
|
205
|
+
' Print only total count of matching entries'
|
206
|
+
],
|
207
|
+
|
208
|
+
fields: [
|
209
|
+
'--fields'.pastel(:green),
|
210
|
+
' Print only Available fields for selected files'
|
211
|
+
],
|
212
|
+
|
213
|
+
slice: [
|
214
|
+
'--slice'.pastel(:green),
|
215
|
+
' Extract specific fields from entries (slice multiple with comma)',
|
216
|
+
' Ex: --slice=path or --slice=path,params'
|
217
|
+
|
218
|
+
],
|
219
|
+
|
220
|
+
except: [
|
221
|
+
'--except'.pastel(:green),
|
222
|
+
' Exclude specific fields (except multiple with comma)',
|
223
|
+
' Ex: --except=params --except=params,path'
|
224
|
+
|
225
|
+
],
|
226
|
+
|
227
|
+
exists: [
|
228
|
+
'--exists'.pastel(:green),
|
229
|
+
' Ensure field exists regardless of contents',
|
230
|
+
' Ex: --exists=params --exists=params,path'
|
231
|
+
|
232
|
+
],
|
233
|
+
|
234
|
+
stats: [
|
235
|
+
'--stats'.pastel(:green),
|
236
|
+
' Order/Count occurrances by field. Combine with `truncate` for key names',
|
237
|
+
' Ex: --stats=params --except=params,path'
|
238
|
+
|
239
|
+
],
|
240
|
+
|
241
|
+
uniq: [
|
242
|
+
'--uniq'.pastel(:green),
|
243
|
+
' Show unique values only',
|
244
|
+
' Ex: --uniq=params --uniq=params,path'
|
245
|
+
|
246
|
+
],
|
247
|
+
|
248
|
+
pluck: [
|
249
|
+
'--pluck'.pastel(:green),
|
250
|
+
' Extract values from entries',
|
251
|
+
' Ex: --pluck=params --pluck=params,path'
|
252
|
+
|
253
|
+
],
|
254
|
+
|
255
|
+
archive: [
|
256
|
+
'--archive'.pastel(:green),
|
257
|
+
' Limit to specific archvie name (partial matching /inclusive). Matching SOS tar.gz name',
|
258
|
+
' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
|
259
|
+
|
260
|
+
],
|
261
|
+
|
262
|
+
sort: [
|
263
|
+
'--sort'.pastel(:green),
|
264
|
+
' Sort by multiple fields',
|
265
|
+
' Ex: --sort=duration_s,db_duration_s'
|
266
|
+
|
267
|
+
],
|
268
|
+
|
269
|
+
reverse: [
|
270
|
+
'--reverse'.pastel(:green),
|
271
|
+
' Reverse all results',
|
272
|
+
' Ex: --reverse'
|
273
|
+
|
274
|
+
],
|
275
|
+
|
276
|
+
combine: [
|
277
|
+
'--combine'.pastel(:green),
|
278
|
+
' Omit archive identifier dividers. Useful with sort or time filters',
|
279
|
+
' Ex: --combine'
|
280
|
+
|
281
|
+
],
|
282
|
+
|
283
|
+
case: [
|
284
|
+
'--case'.pastel(:green),
|
285
|
+
' Exact case match. Defaults to case insensitive',
|
286
|
+
' Ex: --case; --name=Jon, --name=jane --case'
|
287
|
+
|
288
|
+
],
|
289
|
+
|
290
|
+
exact: [
|
291
|
+
'--exact'.pastel(:green),
|
292
|
+
' Exact parameter/value match. Defaults to partial match',
|
293
|
+
' Ex: --field=CommonPartial --exact'
|
294
|
+
|
295
|
+
],
|
296
|
+
|
297
|
+
start: [
|
298
|
+
'--start'.pastel(:green),
|
299
|
+
' Show events after specified time. Filtered by the `time` field',
|
300
|
+
' Use with `--end` for between selections',
|
301
|
+
' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
|
302
|
+
|
303
|
+
],
|
304
|
+
|
305
|
+
end: [
|
306
|
+
'--end'.pastel(:green),
|
307
|
+
' Show events before specified time. Filtered by the `time` field',
|
308
|
+
' Use with `--start` for between selections',
|
309
|
+
' Ex: log filter --end="2021-06-22"'
|
310
|
+
],
|
311
|
+
|
312
|
+
time_zone: [
|
313
|
+
'--time_zone'.pastel(:green),
|
314
|
+
' Manipulate the `time` field into a specific timezone',
|
315
|
+
' Ex: log filter --time_zone=EDT'
|
316
|
+
|
317
|
+
],
|
318
|
+
|
319
|
+
text: [
|
320
|
+
'--text'.pastel(:green),
|
321
|
+
' Full entry text searching (slow)',
|
322
|
+
' --text="anything here"'
|
323
|
+
],
|
324
|
+
|
325
|
+
table_style: [
|
326
|
+
'--table_style'.pastel(:green),
|
327
|
+
' Renderer used for formatted output. basic, ascii, or unicode(default)',
|
328
|
+
' Ex: log filter --table_style=base'
|
329
|
+
],
|
330
|
+
|
331
|
+
truncate: [
|
332
|
+
'--truncate'.pastel(:green),
|
333
|
+
' Truncate field length. On by default (4 rows). Performance issues!',
|
334
|
+
' Disable with --truncate=0'.pastel(:bright_red),
|
335
|
+
' Ex: --truncate=200, --truncate=2048"'
|
336
|
+
],
|
337
|
+
|
338
|
+
field: [
|
339
|
+
'Field Searching'.pastel(:blue),
|
340
|
+
' --[key]=[value]',
|
341
|
+
' Search in key for value',
|
342
|
+
' Example: --path=mirror/pull'
|
343
|
+
],
|
344
|
+
|
345
|
+
ls: [
|
346
|
+
'Search specific logs'.pastel(:blue),
|
347
|
+
' Any non dash parameters will be the log list to search from',
|
348
|
+
" Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.pastel(:yellow)}` for log names"
|
349
|
+
],
|
350
|
+
|
351
|
+
examples: [
|
352
|
+
'Example Queries'.pastel(:blue),
|
353
|
+
" Also see #{'examples'.pastel(:bright_blue)} for even more examples",
|
354
|
+
' log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message',
|
355
|
+
' log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
|
356
|
+
]
|
357
|
+
|
358
|
+
}
|
359
|
+
end
|
360
|
+
# rubocop:enable Metrics/MethodLength,Metrics/ModuleLength
|
160
361
|
end
|
161
362
|
end
|
162
363
|
end
|
data/lib/greenhat/shell/log.rb
CHANGED
@@ -47,6 +47,7 @@ module GreenHat
|
|
47
47
|
puts ' filter'.pastel(:green)
|
48
48
|
puts " Primary way for log searching within greenhat. See #{'filter_help'.pastel(:blue)}"
|
49
49
|
puts ' Time, round, slice/except, and/or, stats, uniq, sort'
|
50
|
+
puts " #{'filter_help'.pastel(:blue)} supports filtering Ex: #{'filter_help stats'.pastel(:blue)}"
|
50
51
|
puts
|
51
52
|
|
52
53
|
puts ' show'.pastel(:green)
|
@@ -62,8 +63,16 @@ module GreenHat
|
|
62
63
|
puts "See #{'examples'.pastel(:bright_blue)} for query examples"
|
63
64
|
end
|
64
65
|
|
65
|
-
def self.filter_help
|
66
|
-
|
66
|
+
def self.filter_help(args = {})
|
67
|
+
if args.empty?
|
68
|
+
ShellHelper::Filter.help
|
69
|
+
else
|
70
|
+
list = ShellHelper::Filter.help_index.select do |k, _v|
|
71
|
+
k.to_s.include? args.first
|
72
|
+
end
|
73
|
+
|
74
|
+
puts list.values.map { |x| x.join("\n") }.join("\n\n")
|
75
|
+
end
|
67
76
|
end
|
68
77
|
|
69
78
|
def self.ls(args = [])
|
@@ -0,0 +1,356 @@
|
|
1
|
+
module GreenHat
|
2
|
+
# Root Level Shell / Report Helper
|
3
|
+
module Shell
|
4
|
+
def self.markdown_report(raw)
|
5
|
+
_files, flags, _args = Args.parse(raw)
|
6
|
+
|
7
|
+
archives = if flags.archive
|
8
|
+
Archive.all.select do |archive|
|
9
|
+
flags.archive.any? { |x| archive.name.include? x.to_s }
|
10
|
+
end
|
11
|
+
else
|
12
|
+
Archive.all
|
13
|
+
end
|
14
|
+
|
15
|
+
ShellHelper.show(archives.map(&:report_markdown).map(&:show).flatten, flags)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module GreenHat
|
21
|
+
# Report Generator Helper
|
22
|
+
# rubocop:disable Metrics/ClassLength
|
23
|
+
class ReportMarkdown
|
24
|
+
include ActionView::Helpers::NumberHelper
|
25
|
+
|
26
|
+
attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
|
27
|
+
:timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
|
28
|
+
:production_log, :api_log, :application_log, :sidekiq_log,
|
29
|
+
:exceptions_log, :gitaly_log, :free_m, :disk_free
|
30
|
+
|
31
|
+
# Find Needed Files for Report
|
32
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
33
|
+
def initialize(archive)
|
34
|
+
self.archive = archive
|
35
|
+
self.host = archive.things.find { |x| x.name == 'hostname' }
|
36
|
+
self.os_release = archive.things.find { |x| x.name == 'etc/os-release' }
|
37
|
+
self.selinux_status = archive.things.find { |x| x.name == 'sestatus' }
|
38
|
+
self.cpu = archive.things.find { |x| x.name == 'lscpu' }
|
39
|
+
self.uname = archive.things.find { |x| x.name == 'uname' }
|
40
|
+
self.timedatectl = archive.things.find { |x| x.name == 'timedatectl' }
|
41
|
+
self.uptime = archive.things.find { |x| x.name == 'uptime' }
|
42
|
+
self.meminfo = archive.things.find { |x| x.name == 'meminfo' }
|
43
|
+
self.free_m = archive.things.find { |x| x.name == 'free_m' }
|
44
|
+
self.gitlab_manifest = archive.things.find { |x| x.name == 'gitlab/version-manifest.json' }
|
45
|
+
self.gitlab_status = archive.things.find { |x| x.name == 'gitlab_status' }
|
46
|
+
self.production_log = archive.things.find { |x| x.name == 'gitlab-rails/production_json.log' }
|
47
|
+
self.api_log = archive.things.find { |x| x.name == 'gitlab-rails/api_json.log' }
|
48
|
+
self.application_log = archive.things.find { |x| x.name == 'gitlab-rails/application_json.log' }
|
49
|
+
self.exceptions_log = archive.things.find { |x| x.name == 'gitlab-rails/exceptions_json.log' }
|
50
|
+
self.gitaly_log = archive.things.find { |x| x.name == 'gitaly/current' }
|
51
|
+
self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
|
52
|
+
self.disk_free = archive.things.find { |x| x.name == 'df_h' }
|
53
|
+
end
|
54
|
+
|
55
|
+
def show
|
56
|
+
output = [
|
57
|
+
archive.friendly_name,
|
58
|
+
''
|
59
|
+
]
|
60
|
+
|
61
|
+
# OS
|
62
|
+
output << "**OS**\n"
|
63
|
+
|
64
|
+
output << collect_host
|
65
|
+
output << ''
|
66
|
+
|
67
|
+
# Memory
|
68
|
+
if meminfo || free_m
|
69
|
+
output << "**Memory**\n"
|
70
|
+
# output << memory_perc if meminfo
|
71
|
+
output << memory_free if free_m
|
72
|
+
output << ''
|
73
|
+
end
|
74
|
+
|
75
|
+
# Disk
|
76
|
+
if disk_free
|
77
|
+
output << disks
|
78
|
+
output << ''
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gitlab
|
82
|
+
output << "**GitLab**\n" if gitlab_manifest || gitlab_status
|
83
|
+
output << gitlab_version if gitlab_manifest
|
84
|
+
output << gitlab_services if gitlab_status
|
85
|
+
|
86
|
+
output << ''
|
87
|
+
|
88
|
+
output << "**Errors**\n" if production_log || api_log || application_log || sidekiq_log
|
89
|
+
output << collect_errors
|
90
|
+
|
91
|
+
# Final Space / Return
|
92
|
+
output << ''
|
93
|
+
output
|
94
|
+
end
|
95
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
96
|
+
|
97
|
+
def collect_host
|
98
|
+
output = []
|
99
|
+
output << hostname if host
|
100
|
+
output << distro if os_release
|
101
|
+
output << selinux if selinux_status
|
102
|
+
# output << arch if cpu
|
103
|
+
output << kernel if uname
|
104
|
+
output << sys_time if timedatectl
|
105
|
+
output << sys_uptime if uptime
|
106
|
+
output << load_average if uptime && cpu
|
107
|
+
|
108
|
+
groups = output.each_slice((output.size / 2.to_f).round).to_a
|
109
|
+
|
110
|
+
table = TTY::Table.new do |t|
|
111
|
+
loop do
|
112
|
+
break if groups.all?(&:empty?)
|
113
|
+
|
114
|
+
t << groups.map(&:shift)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
119
|
+
end
|
120
|
+
|
121
|
+
def collect_errors
|
122
|
+
output = []
|
123
|
+
output << production_errors if production_log
|
124
|
+
output << application_errors if application_log
|
125
|
+
output << sidekiq_errors if sidekiq_log
|
126
|
+
output << api_errors if api_log
|
127
|
+
output << exception_errors if exceptions_log
|
128
|
+
output << gitaly_errors if gitaly_log
|
129
|
+
|
130
|
+
# Keep Alphabetical Sort
|
131
|
+
groups = output.each_slice((output.size / 3.to_f).round).to_a
|
132
|
+
|
133
|
+
table = TTY::Table.new do |t|
|
134
|
+
loop do
|
135
|
+
break if groups.all?(&:empty?)
|
136
|
+
|
137
|
+
t << groups.map(&:shift)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
142
|
+
end
|
143
|
+
|
144
|
+
def exception_errors
|
145
|
+
count = exceptions_log.data.count
|
146
|
+
|
147
|
+
"Exception: #{count}"
|
148
|
+
end
|
149
|
+
|
150
|
+
def gitaly_errors
|
151
|
+
count = gitaly_log.data.count { |x| x.level == 'error' }
|
152
|
+
|
153
|
+
"Gitaly: #{count}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def production_errors
|
157
|
+
count = production_log.data.count { |x| x.status == 500 }
|
158
|
+
|
159
|
+
"Production: #{count}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def api_errors
|
163
|
+
count = api_log.data.count { |x| x.status == 500 }
|
164
|
+
|
165
|
+
"API: #{count}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def application_errors
|
169
|
+
results = ShellHelper.filter_internal([
|
170
|
+
'gitlab-rails/application_json.log',
|
171
|
+
'--message!="Cannot obtain an exclusive lease"',
|
172
|
+
'--severity=error'
|
173
|
+
].join(' '))
|
174
|
+
|
175
|
+
"Application: #{results.count}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def sidekiq_errors
|
179
|
+
count = sidekiq_log.data.count { |x| x&.severity == 'ERROR' }
|
180
|
+
|
181
|
+
"Sidekiq: #{count}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def gitlab_services
|
185
|
+
[
|
186
|
+
"**Services**\n",
|
187
|
+
"\n",
|
188
|
+
GreenHat::GitLab.services_markdown(archive)
|
189
|
+
].join
|
190
|
+
rescue StandardError => e
|
191
|
+
LogBot.fatal('GitLab Services', message: e.message, backtrace: e.backtrace.first)
|
192
|
+
end
|
193
|
+
|
194
|
+
def gitlab_version
|
195
|
+
"Version: #{gitlab_manifest.data.build_version}\n"
|
196
|
+
end
|
197
|
+
|
198
|
+
def hostname
|
199
|
+
"Hostname: #{host.data.first}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def distro
|
203
|
+
[
|
204
|
+
"Distro: [#{os_release.data.ID}] ",
|
205
|
+
os_release.data.PRETTY_NAME
|
206
|
+
].join
|
207
|
+
end
|
208
|
+
|
209
|
+
def selinux
|
210
|
+
status = selinux_status.data['SELinux status']
|
211
|
+
|
212
|
+
[
|
213
|
+
'SeLinux: ',
|
214
|
+
status,
|
215
|
+
' (',
|
216
|
+
selinux_status.data['Current mode'],
|
217
|
+
')'
|
218
|
+
].join
|
219
|
+
end
|
220
|
+
|
221
|
+
def arch
|
222
|
+
[
|
223
|
+
'Arch: ',
|
224
|
+
cpu.data.Architecture
|
225
|
+
].join
|
226
|
+
end
|
227
|
+
|
228
|
+
def kernel
|
229
|
+
# TODO: Better way to consistently get uname info?
|
230
|
+
value, build = uname.data.first.split[2].split('-')
|
231
|
+
[
|
232
|
+
'Kernel: ',
|
233
|
+
value,
|
234
|
+
" (#{build})"
|
235
|
+
].join
|
236
|
+
end
|
237
|
+
|
238
|
+
# Helper for finding if NTP is enabled
|
239
|
+
def ntp_keys
|
240
|
+
[
|
241
|
+
'Network time on', 'NTP enabled', 'NTP service', 'System clock synchronized'
|
242
|
+
]
|
243
|
+
end
|
244
|
+
|
245
|
+
def sys_time
|
246
|
+
# Ignore if Empty
|
247
|
+
return false if timedatectl.data.nil?
|
248
|
+
|
249
|
+
ntp_statuses = timedatectl.data.slice(*ntp_keys).values.compact
|
250
|
+
|
251
|
+
ntp_status = ntp_statuses.first
|
252
|
+
|
253
|
+
# Fall Back
|
254
|
+
ntp_status ||= 'unknown'
|
255
|
+
|
256
|
+
[
|
257
|
+
'Sys Time: ',
|
258
|
+
timedatectl.data['Local time'],
|
259
|
+
"(ntp: #{ntp_status})"
|
260
|
+
].join
|
261
|
+
end
|
262
|
+
|
263
|
+
# Strip/Simplify Uptime
|
264
|
+
def sys_uptime
|
265
|
+
init = uptime.data.first.split(', load average').first.strip
|
266
|
+
|
267
|
+
"Uptime: #{init.split('up ', 2).last}"
|
268
|
+
end
|
269
|
+
|
270
|
+
def load_average
|
271
|
+
cpu_count = cpu.data['CPU(s)'].to_i
|
272
|
+
intervals = uptime.data.first.split('load average: ', 2).last.split(', ').map(&:to_f)
|
273
|
+
|
274
|
+
# Generate Colorized Text for Output
|
275
|
+
intervals_text = intervals.map do |interval|
|
276
|
+
value = percent(interval, cpu_count)
|
277
|
+
|
278
|
+
"#{interval} (#{value}%)"
|
279
|
+
end
|
280
|
+
|
281
|
+
[
|
282
|
+
'LoadAvg: ',
|
283
|
+
"[CPU #{cpu_count}] ",
|
284
|
+
intervals_text.join(', ')
|
285
|
+
].join
|
286
|
+
end
|
287
|
+
|
288
|
+
# def memory_perc
|
289
|
+
# total = ShellHelper.human_size_to_number(meminfo.data['MemTotal'])
|
290
|
+
# free = ShellHelper.human_size_to_number(meminfo.data['MemFree'])
|
291
|
+
# used = percent((total - free), total)
|
292
|
+
|
293
|
+
# [
|
294
|
+
# title('Usage'),
|
295
|
+
# ' ['.pastel(:bright_black),
|
296
|
+
# '='.pastel(:green) * (used / 2),
|
297
|
+
# ' ' * (50 - used / 2),
|
298
|
+
# ']'.pastel(:bright_black),
|
299
|
+
# " #{100 - percent(free, total)}%".pastel(:green) # Inverse
|
300
|
+
# ].join
|
301
|
+
# end
|
302
|
+
|
303
|
+
def memory_free
|
304
|
+
free = free_m.data.find { |x| x.kind == 'Mem' }
|
305
|
+
|
306
|
+
return unless free
|
307
|
+
|
308
|
+
pad = 6
|
309
|
+
list = [
|
310
|
+
"#{title('Total', pad)} #{number_to_human_size(free.total.to_i * 1024**2)}",
|
311
|
+
"#{title('Used', pad)} #{number_to_human_size(free.used.to_i * 1024**2)}",
|
312
|
+
"#{title('Free', pad)} #{number_to_human_size(free.free.to_i * 1024**2)}",
|
313
|
+
"#{title('Avail', pad)} #{number_to_human_size(free.available.to_i * 1024**2)}"
|
314
|
+
]
|
315
|
+
|
316
|
+
# Keep Alphabetical Sort
|
317
|
+
groups = list.each_slice((list.size / 2.to_f).round).to_a
|
318
|
+
|
319
|
+
table = TTY::Table.new do |t|
|
320
|
+
loop do
|
321
|
+
break if groups.all?(&:empty?)
|
322
|
+
|
323
|
+
t << groups.map(&:shift)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
"```\n#{table.render(:basic, padding: [0, 2, 0, 0])}\n```"
|
328
|
+
end
|
329
|
+
|
330
|
+
def disks
|
331
|
+
# GreenHat::Disk.df({archive: []})
|
332
|
+
file = GreenHat::Disk.df({ archive: [archive.name] })
|
333
|
+
|
334
|
+
disk_list = GreenHat::Disk.markdown_format(file.first, false, 3)
|
335
|
+
|
336
|
+
# Preapre / Indent List
|
337
|
+
[
|
338
|
+
'**Disks**',
|
339
|
+
"\n\n```\n#{disk_list.join("\n")}\n```"
|
340
|
+
].join
|
341
|
+
end
|
342
|
+
|
343
|
+
# ----------------------------
|
344
|
+
# Helpers
|
345
|
+
# ----------------------------
|
346
|
+
def percent(value, total)
|
347
|
+
((value / total.to_f) * 100).round
|
348
|
+
end
|
349
|
+
|
350
|
+
# Helper to Make Cyan Titles
|
351
|
+
def title(name, ljust = 16)
|
352
|
+
"#{name}:".ljust(ljust)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
# rubocop:enable Metrics/ClassLength
|
356
|
+
end
|
@@ -25,7 +25,8 @@ module GreenHat
|
|
25
25
|
|
26
26
|
attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
|
27
27
|
:timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
|
28
|
-
:production_log, :api_log, :application_log, :sidekiq_log,
|
28
|
+
:production_log, :api_log, :application_log, :sidekiq_log,
|
29
|
+
:exceptions_log, :gitaly_log, :free_m, :disk_free
|
29
30
|
|
30
31
|
# Find Needed Files for Report
|
31
32
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
@@ -45,6 +46,8 @@ module GreenHat
|
|
45
46
|
self.production_log = archive.things.find { |x| x.name == 'gitlab-rails/production_json.log' }
|
46
47
|
self.api_log = archive.things.find { |x| x.name == 'gitlab-rails/api_json.log' }
|
47
48
|
self.application_log = archive.things.find { |x| x.name == 'gitlab-rails/application_json.log' }
|
49
|
+
self.exceptions_log = archive.things.find { |x| x.name == 'gitlab-rails/exceptions_json.log' }
|
50
|
+
self.gitaly_log = archive.things.find { |x| x.name == 'gitaly/current' }
|
48
51
|
self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
|
49
52
|
self.disk_free = archive.things.find { |x| x.name == 'df_h' }
|
50
53
|
end
|
@@ -86,9 +89,11 @@ module GreenHat
|
|
86
89
|
output << gitlab_services if gitlab_status
|
87
90
|
output << title('Errors') if production_log || api_log || application_log || sidekiq_log
|
88
91
|
output << production_errors if production_log
|
89
|
-
output << api_errors if api_log
|
90
92
|
output << application_errors if application_log
|
91
93
|
output << sidekiq_errors if sidekiq_log
|
94
|
+
output << api_errors if api_log
|
95
|
+
output << exception_errors if exceptions_log
|
96
|
+
output << gitaly_errors if gitaly_log
|
92
97
|
|
93
98
|
# Final Space / Return
|
94
99
|
output << ''
|
@@ -96,6 +101,26 @@ module GreenHat
|
|
96
101
|
end
|
97
102
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
98
103
|
|
104
|
+
def exception_errors
|
105
|
+
count = exceptions_log.data.count
|
106
|
+
color = count.zero? ? :green : :red
|
107
|
+
|
108
|
+
[
|
109
|
+
title(' Exception', :bright_red, 18),
|
110
|
+
count.to_s.pastel(color)
|
111
|
+
].join
|
112
|
+
end
|
113
|
+
|
114
|
+
def gitaly_errors
|
115
|
+
count = gitaly_log.data.count { |x| x.level == 'error' }
|
116
|
+
color = count.zero? ? :green : :red
|
117
|
+
|
118
|
+
[
|
119
|
+
title(' Gitaly', :bright_red, 18),
|
120
|
+
count.to_s.pastel(color)
|
121
|
+
].join
|
122
|
+
end
|
123
|
+
|
99
124
|
def production_errors
|
100
125
|
count = production_log.data.count { |x| x.status == 500 }
|
101
126
|
color = count.zero? ? :green : :red
|
@@ -117,7 +142,13 @@ module GreenHat
|
|
117
142
|
end
|
118
143
|
|
119
144
|
def application_errors
|
120
|
-
|
145
|
+
results = ShellHelper.filter_internal([
|
146
|
+
'gitlab-rails/application_json.log',
|
147
|
+
'--message!="Cannot obtain an exclusive lease"',
|
148
|
+
'--severity=error'
|
149
|
+
].join(' '))
|
150
|
+
|
151
|
+
count = results.count { |x| x&.severity == 'ERROR' }
|
121
152
|
color = count.zero? ? :green : :red
|
122
153
|
|
123
154
|
[
|
@@ -150,8 +150,18 @@ module GreenHat
|
|
150
150
|
format_table_entry(flags, val)
|
151
151
|
end
|
152
152
|
|
153
|
+
# Internal Query Helper
|
154
|
+
# query = 'gitlab-rails/application_json.log --message!="Cannot obtain an exclusive lease" --severity=error'
|
155
|
+
# ShellHelper.filter_internal(query)
|
156
|
+
def self.filter_internal(search = '')
|
157
|
+
files, flags, args = Args.parse(Shellwords.split(search))
|
158
|
+
flags[:combine] = true
|
159
|
+
|
160
|
+
ShellHelper.filter_start(files, flags, args)
|
161
|
+
end
|
162
|
+
|
153
163
|
# Main Entry Point for Filtering
|
154
|
-
def self.filter_start(files, flags, args)
|
164
|
+
def self.filter_start(files, flags = {}, args = {})
|
155
165
|
# Convert to Things
|
156
166
|
logs = ShellHelper.find_things(files, flags).select(&:processed?)
|
157
167
|
|
@@ -189,7 +199,7 @@ module GreenHat
|
|
189
199
|
# Experimenting with deep clone
|
190
200
|
results = Marshal.load(Marshal.dump(data))
|
191
201
|
results.select! do |row|
|
192
|
-
args.send(flags.logic) do |arg|
|
202
|
+
args.send(flags.logic || :all?) do |arg|
|
193
203
|
filter_row_key(row, arg, flags)
|
194
204
|
end
|
195
205
|
end
|
@@ -43,6 +43,13 @@ module GreenHat
|
|
43
43
|
/dmesg/
|
44
44
|
]
|
45
45
|
},
|
46
|
+
'lets-encrypt/renewal' => {
|
47
|
+
format: :bracket_log,
|
48
|
+
log: true,
|
49
|
+
pattern: [
|
50
|
+
%r{lets-encrypt/renewal}
|
51
|
+
]
|
52
|
+
},
|
46
53
|
'repmgrd/current' => {
|
47
54
|
format: :bracket_log,
|
48
55
|
log: true,
|
@@ -225,7 +232,8 @@ module GreenHat
|
|
225
232
|
format: :json,
|
226
233
|
log: true,
|
227
234
|
pattern: [
|
228
|
-
%r{gitlab-rails/audit_json.log}
|
235
|
+
%r{gitlab-rails/audit_json.log},
|
236
|
+
%r{webservice.log/audit_json.log}
|
229
237
|
]
|
230
238
|
},
|
231
239
|
'gitlab-rails/auth.log' => {
|
@@ -336,6 +344,13 @@ module GreenHat
|
|
336
344
|
%r{nginx/current}
|
337
345
|
]
|
338
346
|
},
|
347
|
+
'nginx-ingress.log' => {
|
348
|
+
format: :kube_nginx,
|
349
|
+
log: true,
|
350
|
+
pattern: [
|
351
|
+
/nginx-ingress.log/
|
352
|
+
]
|
353
|
+
},
|
339
354
|
'nginx/gitlab_pages_access.log' => {
|
340
355
|
format: :nginx,
|
341
356
|
log: true,
|
@@ -422,7 +437,7 @@ module GreenHat
|
|
422
437
|
]
|
423
438
|
},
|
424
439
|
'registry/current' => {
|
425
|
-
format: :
|
440
|
+
format: :time_registry,
|
426
441
|
log: true,
|
427
442
|
pattern: [
|
428
443
|
%r{registry/current},
|
@@ -444,16 +459,18 @@ module GreenHat
|
|
444
459
|
},
|
445
460
|
'puma/puma_stderr.log' => {
|
446
461
|
format: :raw,
|
447
|
-
log:
|
462
|
+
log: false,
|
448
463
|
pattern: [
|
449
|
-
%r{puma/puma_stderr.log}
|
464
|
+
%r{puma/puma_stderr.log},
|
465
|
+
%r{webservice.log/puma.stderr.log}
|
450
466
|
]
|
451
467
|
},
|
452
468
|
'puma/puma_stdout.log' => {
|
453
469
|
format: :json,
|
454
470
|
log: true,
|
455
471
|
pattern: [
|
456
|
-
%r{puma/puma_stdout.log}
|
472
|
+
%r{puma/puma_stdout.log},
|
473
|
+
%r{webservice.log/puma.stdout.log}
|
457
474
|
]
|
458
475
|
},
|
459
476
|
'gitlab-pages/current' => {
|
@@ -745,13 +762,15 @@ module GreenHat
|
|
745
762
|
'gitlab-rails/application.log' => {
|
746
763
|
format: :raw,
|
747
764
|
pattern: [
|
748
|
-
%r{gitlab-rails/application.log}
|
765
|
+
%r{gitlab-rails/application.log},
|
766
|
+
%r{webservice.log/application.log}
|
749
767
|
]
|
750
768
|
},
|
751
769
|
'gitlab-rails/production.log' => {
|
752
770
|
format: :raw,
|
753
771
|
pattern: [
|
754
|
-
%r{gitlab-rails/production.log}
|
772
|
+
%r{gitlab-rails/production.log},
|
773
|
+
%r{webservice.log/production.log}
|
755
774
|
]
|
756
775
|
},
|
757
776
|
'gitlab/version-manifest.txt' => {
|
@@ -765,6 +784,72 @@ module GreenHat
|
|
765
784
|
pattern: [
|
766
785
|
%r{sidekiq/perf.data}
|
767
786
|
]
|
787
|
+
},
|
788
|
+
|
789
|
+
# ======================================================================
|
790
|
+
# KubeSoS TODO Section
|
791
|
+
# Things I am going to shortcut and set to raw for now
|
792
|
+
# ======================================================================
|
793
|
+
# Attempted Parsing
|
794
|
+
'kubesos_json' => {
|
795
|
+
log: true,
|
796
|
+
format: :kube_json,
|
797
|
+
pattern: [
|
798
|
+
/gitaly.log/
|
799
|
+
]
|
800
|
+
},
|
801
|
+
|
802
|
+
'kube_webservice' => {
|
803
|
+
log: false,
|
804
|
+
format: :kube_webservice,
|
805
|
+
pattern: [
|
806
|
+
/^webservice\.log$/
|
807
|
+
]
|
808
|
+
},
|
809
|
+
|
810
|
+
'kubesos' => {
|
811
|
+
format: :raw,
|
812
|
+
pattern: [
|
813
|
+
/all_values.yaml/,
|
814
|
+
%r{webservice.log/sidekiq_client.log},
|
815
|
+
/chart-version/,
|
816
|
+
/configmaps/,
|
817
|
+
/describe_deployments/,
|
818
|
+
/describe_ingress/,
|
819
|
+
/describe_nodes/,
|
820
|
+
/describe_pods/,
|
821
|
+
/describe_pv/,
|
822
|
+
/describe_pvc/,
|
823
|
+
/events/,
|
824
|
+
/get_deployments/,
|
825
|
+
/get_endpoints/,
|
826
|
+
/get_jobs/,
|
827
|
+
/get_pods/,
|
828
|
+
/get_pv/,
|
829
|
+
/get_pvc/,
|
830
|
+
/get_services/,
|
831
|
+
/gitaly.log/,
|
832
|
+
/gitlab-exporter.log/,
|
833
|
+
/gitlab-pages.log/,
|
834
|
+
/gitlab-shell.log/,
|
835
|
+
/grafana.log/,
|
836
|
+
/helm-version/,
|
837
|
+
/kubectl-check/,
|
838
|
+
/migrations.log/,
|
839
|
+
/minio.log/,
|
840
|
+
/nfs-client-provisioner.log/,
|
841
|
+
/operator.log/,
|
842
|
+
/postgresql.log/,
|
843
|
+
/prometheus.log/,
|
844
|
+
/redis.log/,
|
845
|
+
/registry.log/,
|
846
|
+
/secrets/,
|
847
|
+
/sidekiq.log/,
|
848
|
+
/task-runner.log/,
|
849
|
+
/top_nodes/,
|
850
|
+
/top_pods/,
|
851
|
+
/user_supplied_values.yaml/
|
852
|
+
]
|
768
853
|
}
|
769
854
|
}
|
770
855
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# K8s logs seem to have a lot of non-json entries
|
7
|
+
# Extract all json, ignore everything else
|
8
|
+
# ==========================================================================
|
9
|
+
def format_kube_json
|
10
|
+
self.result = raw.map do |row|
|
11
|
+
# Skip all non-json
|
12
|
+
next unless row.first == '{'
|
13
|
+
|
14
|
+
result = begin
|
15
|
+
Oj.load row
|
16
|
+
rescue EncodingError
|
17
|
+
puts
|
18
|
+
next
|
19
|
+
end
|
20
|
+
|
21
|
+
# Parsing Time
|
22
|
+
format_json_traverse result
|
23
|
+
|
24
|
+
result.sort.to_h
|
25
|
+
rescue StandardError => e
|
26
|
+
# TODO: Background Logger?
|
27
|
+
e.message
|
28
|
+
LogBot.warn('JSON Parse', e.message)
|
29
|
+
next
|
30
|
+
end.compact
|
31
|
+
|
32
|
+
:ok
|
33
|
+
end
|
34
|
+
# =========================================================================
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Formatters
|
7
|
+
# ==========================================================================
|
8
|
+
def format_kube_nginx
|
9
|
+
self.result = raw.map do |row|
|
10
|
+
ip, _sym, remote_user, rest = row.split(' ', 4)
|
11
|
+
|
12
|
+
time, rest = rest.split(']', 2)
|
13
|
+
time = Time.strptime(time, '[%d/%b/%Y:%H:%M:%S %z')
|
14
|
+
|
15
|
+
verb, status, bytes, http_referer, http_user_agent, gzip_ratio = Shellwords.split(rest)
|
16
|
+
|
17
|
+
method, path, http_version = verb.split
|
18
|
+
|
19
|
+
{
|
20
|
+
remote_addr: ip,
|
21
|
+
remote_user: remote_user,
|
22
|
+
method: method,
|
23
|
+
path: path,
|
24
|
+
status: status,
|
25
|
+
bytes: bytes,
|
26
|
+
http_version: http_version,
|
27
|
+
http_referer: http_referer,
|
28
|
+
http_user_agent: http_user_agent,
|
29
|
+
gzip_ratio: gzip_ratio,
|
30
|
+
time: time
|
31
|
+
}
|
32
|
+
|
33
|
+
# Fall back for malformed logs
|
34
|
+
rescue StandardError
|
35
|
+
{ message: row }
|
36
|
+
end
|
37
|
+
|
38
|
+
:ok
|
39
|
+
end
|
40
|
+
|
41
|
+
# rubocop:disable Layout/LineLength
|
42
|
+
# NGINX Conf: /var/opt/gitlab/nginx/conf/nginx.conf
|
43
|
+
# log_format gitlab_access '$remote_addr - $remote_user [$time_local] "$request_method $filtered_request_uri $server_protocol" $status $body_bytes_sent "$filtered_http_referer" "$http_user_agent" $gzip_ratio';
|
44
|
+
# rubocop:enable Layout/LineLength
|
45
|
+
|
46
|
+
# ==========================================================================
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Gitlab Tail Formatter
|
7
|
+
# ==========================================================================
|
8
|
+
def format_kube_webservice
|
9
|
+
# Revert to raw for cats
|
10
|
+
self.kind = :raw
|
11
|
+
|
12
|
+
output = {}
|
13
|
+
current_log = nil
|
14
|
+
|
15
|
+
raw.each do |line|
|
16
|
+
next if line.blank?
|
17
|
+
|
18
|
+
if line.include? '*** '
|
19
|
+
current_log = /\*\*\* (.+?) \*\*\*/.match(line).captures.first
|
20
|
+
else
|
21
|
+
output[current_log] ||= [] unless current_log.nil?
|
22
|
+
output[current_log].push line unless current_log.nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Remove Empty Entries
|
27
|
+
output.reject { |_k, v| v.empty? }
|
28
|
+
|
29
|
+
# Root Dir
|
30
|
+
root_dir = "#{$TMP}/#{name}"
|
31
|
+
Dir.mkdir(root_dir)
|
32
|
+
|
33
|
+
# Write Files / Create Things
|
34
|
+
output.each do |k, v|
|
35
|
+
file_name = k.gsub('/var/log/gitlab/', '')
|
36
|
+
|
37
|
+
dir = "#{root_dir}/gitlab-rails"
|
38
|
+
Dir.mkdir(dir) unless File.exist?(dir)
|
39
|
+
|
40
|
+
File.write("#{root_dir}/#{file_name}", v.join("\n"))
|
41
|
+
|
42
|
+
# Thing Setup
|
43
|
+
archive.things_create(file: "#{root_dir}/#{file_name}").setup
|
44
|
+
end
|
45
|
+
|
46
|
+
# Link
|
47
|
+
self.result = raw
|
48
|
+
end
|
49
|
+
# ==========================================================================
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Top
|
2
|
+
module GreenHat
|
3
|
+
# Log
|
4
|
+
module Formatters
|
5
|
+
# ==========================================================================
|
6
|
+
# Registry Split
|
7
|
+
# ==========================================================================
|
8
|
+
# https://docs.docker.com/registry/configuration/#log
|
9
|
+
# Formatters text, json
|
10
|
+
# registry['log_formatter'] = "json"
|
11
|
+
# TODO: Logstash (Not working in 14.3)
|
12
|
+
|
13
|
+
def format_time_registry
|
14
|
+
self.result = raw.map do |row|
|
15
|
+
result = row[0] == '{' ? registry_json(row) : registry_shell_words(row)
|
16
|
+
|
17
|
+
# Timestamp Parsing
|
18
|
+
result.ts = Time.parse result.ts if result.key? 'ts'
|
19
|
+
result.time = Time.parse(result.time)
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def registry_shell_words(row)
|
26
|
+
time, msg = row.split(' ', 2)
|
27
|
+
|
28
|
+
output = Shellwords.split(msg).each_with_object({}) do |x, h|
|
29
|
+
key, value = x.split('=')
|
30
|
+
next if value.nil?
|
31
|
+
|
32
|
+
h[key.to_sym] = value.numeric? ? value.to_f : value
|
33
|
+
end
|
34
|
+
|
35
|
+
output[:time] = time
|
36
|
+
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
def registry_json(msg)
|
41
|
+
Oj.load(msg)
|
42
|
+
rescue EncodingError
|
43
|
+
{ message: msg }
|
44
|
+
end
|
45
|
+
# ==========================================================================
|
46
|
+
end
|
47
|
+
end
|
data/lib/greenhat/version.rb
CHANGED
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.3.
|
4
|
+
version: 0.3.4
|
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-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amazing_print
|
@@ -425,6 +425,7 @@ files:
|
|
425
425
|
- lib/greenhat/shell/help.rb
|
426
426
|
- lib/greenhat/shell/list.rb
|
427
427
|
- lib/greenhat/shell/log.rb
|
428
|
+
- lib/greenhat/shell/markdown.rb
|
428
429
|
- lib/greenhat/shell/memory.rb
|
429
430
|
- lib/greenhat/shell/network.rb
|
430
431
|
- lib/greenhat/shell/page.rb
|
@@ -444,9 +445,13 @@ files:
|
|
444
445
|
- lib/greenhat/thing/formatters/gitlab_status.rb
|
445
446
|
- lib/greenhat/thing/formatters/json.rb
|
446
447
|
- lib/greenhat/thing/formatters/json_shellwords.rb
|
448
|
+
- lib/greenhat/thing/formatters/kube_json.rb
|
449
|
+
- lib/greenhat/thing/formatters/kube_nginx.rb
|
450
|
+
- lib/greenhat/thing/formatters/kube_webservice.rb
|
447
451
|
- lib/greenhat/thing/formatters/multiline_json.rb
|
448
452
|
- lib/greenhat/thing/formatters/nginx.rb
|
449
453
|
- lib/greenhat/thing/formatters/raw.rb
|
454
|
+
- lib/greenhat/thing/formatters/registry.rb
|
450
455
|
- lib/greenhat/thing/formatters/shellwords.rb
|
451
456
|
- lib/greenhat/thing/formatters/syslog.rb
|
452
457
|
- lib/greenhat/thing/formatters/table.rb
|