greenhat 0.2.0 → 0.3.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/greenhat/accessors/disk.rb +13 -13
  3. data/lib/greenhat/accessors/gitlab.rb +75 -0
  4. data/lib/greenhat/accessors/memory.rb +10 -10
  5. data/lib/greenhat/accessors/process.rb +4 -0
  6. data/lib/greenhat/cli.rb +147 -58
  7. data/lib/greenhat/color.rb +27 -0
  8. data/lib/greenhat/logbot.rb +9 -9
  9. data/lib/greenhat/settings.rb +27 -7
  10. data/lib/greenhat/shell/args.rb +146 -0
  11. data/lib/greenhat/shell/cat.rb +25 -73
  12. data/lib/greenhat/shell/color_string.rb +8 -8
  13. data/lib/greenhat/shell/disk.rb +31 -4
  14. data/lib/greenhat/shell/faststats.rb +103 -56
  15. data/lib/greenhat/shell/field_helper.rb +75 -0
  16. data/lib/greenhat/shell/filter_help.rb +162 -0
  17. data/lib/greenhat/shell/gitlab.rb +61 -2
  18. data/lib/greenhat/shell/help.rb +98 -15
  19. data/lib/greenhat/shell/list.rb +46 -0
  20. data/lib/greenhat/shell/log.rb +118 -104
  21. data/lib/greenhat/shell/page.rb +11 -5
  22. data/lib/greenhat/shell/process.rb +29 -17
  23. data/lib/greenhat/shell/report.rb +37 -47
  24. data/lib/greenhat/shell/shell_helper.rb +661 -0
  25. data/lib/greenhat/shell.rb +23 -9
  26. data/lib/greenhat/thing/file_types.rb +31 -5
  27. data/lib/greenhat/thing/formatters/json_shellwords.rb +0 -3
  28. data/lib/greenhat/thing/formatters/nginx.rb +44 -0
  29. data/lib/greenhat/thing/helpers.rb +4 -4
  30. data/lib/greenhat/thing/kind.rb +9 -2
  31. data/lib/greenhat/thing/spinner.rb +3 -3
  32. data/lib/greenhat/thing.rb +25 -3
  33. data/lib/greenhat/tty/columns.rb +4 -0
  34. data/lib/greenhat/version.rb +1 -1
  35. data/lib/greenhat.rb +15 -14
  36. metadata +38 -18
  37. data/lib/greenhat/shell/filter.rb +0 -128
  38. data/lib/greenhat/shell/helper.rb +0 -584
@@ -0,0 +1,75 @@
1
+ module GreenHat
2
+ # Common Helpers
3
+ module FieldHelper
4
+ def self.fields_find(files, word, flags = {})
5
+ fields = ShellHelper.find_things(files, flags).map(&:fields).flatten.uniq
6
+
7
+ if word.blank?
8
+ puts 'Possible Fields:'.pastel(:bright_blue)
9
+ puts ShellHelper.field_table(fields)
10
+
11
+ return [] # Empty Result
12
+ end
13
+
14
+ list_select(fields, word)
15
+ end
16
+
17
+ def self.list_select(list, word)
18
+ list.select! { |x| x[/^#{Regexp.escape(word)}/] }
19
+ end
20
+
21
+ def self.filter_flags(word)
22
+ if word.blank?
23
+ puts 'Filter Options:'.pastel(:bright_blue)
24
+ puts ShellHelper.field_table(filter_opts, 6)
25
+ puts
26
+
27
+ return []
28
+ end
29
+
30
+ list_select(filter_opts, word)
31
+ end
32
+
33
+ def self.filter_opts
34
+ %w[
35
+ archive case combine end exact except exists json limit or page pluck
36
+ raw reverse round slice sort start stats table_style text time_zone
37
+ total truncate uniq
38
+ ]
39
+ end
40
+
41
+ def self.filter_auto_completes
42
+ %w[
43
+ except exists pluck slice sort stats uniq
44
+ ]
45
+ end
46
+
47
+ def self.field_auto_complete?(word)
48
+ return false if word.blank?
49
+
50
+ filter_auto_completes.include? word.split('=', 2).first
51
+ end
52
+
53
+ def self.field_auto_complete(word, files, flags = {})
54
+ # Prevent weird dupes
55
+ return nil if word[-1] == ','
56
+
57
+ # Command Manipulation
58
+ cmd, fields = word.split('=', 2)
59
+ complete = fields.split(',')[0..-2]
60
+ auto = fields.split(',').last
61
+
62
+ # Field Finder
63
+ matches = fields_find(files, auto, flags)
64
+
65
+ if matches.count == 1
66
+ "--#{cmd}=#{(complete + matches).join(',')}"
67
+ elsif matches.count > 1
68
+ puts "#{'Field Options:'.pastel(:bright_blue)} #{matches.join(' ').pastel(:bright_green)}"
69
+
70
+ list = [Cli.common_substr(matches.map(&:to_s))]
71
+ "--#{cmd}=#{(complete + list).join(',')}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,162 @@
1
+ module GreenHat
2
+ # CLI Helper
3
+ module ShellHelper
4
+ # Unify Filter / Filter Help
5
+ module Filter
6
+ # rubocop:disable Metrics/MethodLength
7
+ def self.help
8
+ puts "\u2500".pastel(:cyan) * 20
9
+ puts 'Filter'.pastel(:yellow)
10
+ puts "\u2500".pastel(:cyan) * 20
11
+
12
+ puts 'Options'.pastel(:blue)
13
+ puts '--raw'.pastel(:green)
14
+ puts ' Disable formatting and page/less'
15
+ puts
16
+
17
+ puts '--page'.pastel(:green)
18
+ puts ' Specifically enable or disable paging'
19
+ puts ' E.g. --page (default to true), --page=true, --page=false'
20
+ puts
21
+
22
+ puts '--round'.pastel(:green)
23
+ puts ' Attempt to round all integers. Default: 2.'
24
+ puts ' E.g. --round, --round=3, --round=0'
25
+ puts
26
+
27
+ puts '--limit'.pastel(:green)
28
+ puts ' Limit total output lines. Disabled by default. Default without value is based on screen height'
29
+ puts ' E.g. --limit, --limit=5'
30
+ puts
31
+
32
+ puts '--json'.pastel(:green)
33
+ puts ' Print output back into JSON'
34
+ puts
35
+
36
+ puts '--or'.pastel(:green)
37
+ puts ' Filters will use OR instead of AND (all match vs any match)'
38
+ puts
39
+
40
+ puts '--total'.pastel(:green)
41
+ puts ' Print only total count of matching entries'
42
+ puts
43
+
44
+ puts '--fields'.pastel(:green)
45
+ puts ' Print only Available fields for selected files'
46
+ puts
47
+
48
+ puts '--slice'.pastel(:green)
49
+ puts ' Extract specific fields from entries (slice multiple with comma)'
50
+ puts ' Ex: --slice=path or --slice=path,params'
51
+ puts
52
+
53
+ puts '--except'.pastel(:green)
54
+ puts ' Exclude specific fields (except multiple with comma)'
55
+ puts ' Ex: --except=params --except=params,path'
56
+ puts
57
+
58
+ puts '--exists'.pastel(:green)
59
+ puts ' Ensure field exists regardless of contents'
60
+ puts ' Ex: --exists=params --exists=params,path'
61
+ puts
62
+
63
+ puts '--stats'.pastel(:green)
64
+ puts ' Order/Count occurrances by field. Combine with `truncate` for key names'
65
+ puts ' Ex: --stats=params --except=params,path'
66
+ puts
67
+
68
+ puts '--uniq'.pastel(:green)
69
+ puts ' Show unique values only'
70
+ puts ' Ex: --uniq=params --uniq=params,path'
71
+ puts
72
+
73
+ puts '--pluck'.pastel(:green)
74
+ puts ' Extract values from entries'
75
+ puts ' Ex: --pluck=params --pluck=params,path'
76
+ puts
77
+
78
+ puts '--archive'.pastel(:green)
79
+ puts ' Limit to specific archvie name (partial matching /inclusive). Matching SOS tar.gz name'
80
+ puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
81
+ puts
82
+
83
+ puts '--sort'.pastel(:green)
84
+ puts ' Sort by multiple fields'
85
+ puts ' Ex: --sort=duration_s,db_duration_s'
86
+ puts
87
+
88
+ puts '--reverse'.pastel(:green)
89
+ puts ' Reverse all results'
90
+ puts ' Ex: --reverse'
91
+ puts
92
+
93
+ puts '--combine'.pastel(:green)
94
+ puts ' Omit archive identifier dividers. Useful with sort or time filters'
95
+ puts ' Ex: --combine'
96
+ puts
97
+
98
+ puts '--case'.pastel(:green)
99
+ puts ' Exact case match. Defaults to case insensitive'
100
+ puts ' Ex: --case; --name=Jon, --name=jane --case'
101
+ puts
102
+
103
+ puts '--exact'.pastel(:green)
104
+ puts ' Exact parameter/value match. Defaults to partial match'
105
+ puts ' Ex: --field=CommonPartial --exact'
106
+ puts
107
+
108
+ puts '--start'.pastel(:green)
109
+ puts ' Show events after specified time. Filtered by the `time` field'
110
+ puts ' Use with `--end` for between selections'
111
+ puts ' Ex: log filter --start="2021-06-22 14:44 UTC" --end="2021-06-22 14:45 UTC"'
112
+ puts
113
+
114
+ puts '--end'.pastel(:green)
115
+ puts ' Show events before specified time. Filtered by the `time` field'
116
+ puts ' Use with `--start` for between selections'
117
+ puts ' Ex: log filter --end="2021-06-22"'
118
+ puts
119
+
120
+ puts '--time_zone'.pastel(:green)
121
+ puts ' Manipulate the `time` field into a specific timezone'
122
+ puts ' Ex: log filter --time_zone=EDT'
123
+ puts
124
+
125
+ puts '--text'.pastel(:green)
126
+ puts ' Full entry text searching (slow)'
127
+ puts ' --text="anything here"'
128
+ puts
129
+
130
+ puts '--table_style'.pastel(:green)
131
+ puts ' Renderer used for formatted output. basic, ascii, or unicode(default)'
132
+ puts ' Ex: log filter --table_style=base'
133
+ puts
134
+
135
+ puts '--truncate'.pastel(:green)
136
+ puts ' Truncate field length. On by default (4 rows). Performance issues!'
137
+ puts ' Disable with --truncate=0'.pastel(:bright_red)
138
+ puts ' Ex: --truncate=200, --truncate=2048"'
139
+ puts
140
+
141
+ puts 'Field Searching'.pastel(:blue)
142
+ puts ' --[key]=[value]'
143
+ puts ' Search in key for value'
144
+ puts ' Example: --path=mirror/pull'
145
+ puts
146
+
147
+ puts 'Search specific logs'.pastel(:blue)
148
+ puts ' Any non dash parameters will be the log list to search from'
149
+ puts " Ex: log filter --path=api sidekiq/current (hint: use `#{'ls'.pastel(:yellow)}` for log names"
150
+ puts
151
+
152
+ puts 'Example Queries'.pastel(:blue)
153
+ puts " Also see #{'examples'.pastel(:bright_blue)} for even more examples"
154
+ puts ' log filter --class=BuildFinishedWorker sidekiq/current --slice=time,message'
155
+ puts ' log filter gitlab-rails/api_json.log --slice=ua --uniq=ua --ua=gitlab-runner'
156
+
157
+ puts
158
+ end
159
+ # rubocop:enable Metrics/MethodLength
160
+ end
161
+ end
162
+ end
@@ -3,16 +3,75 @@ module GreenHat
3
3
  module Shell
4
4
  # Logs
5
5
  module Gitlab
6
+ def self.ls
7
+ help
8
+ end
9
+
10
+ def self.help
11
+ puts "\u2500".pastel(:cyan) * 20
12
+ puts "#{'GitLab'.pastel(:yellow)} - Tanuki power"
13
+ puts "\u2500".pastel(:cyan) * 20
14
+
15
+ puts 'Command Summary'.pastel(:blue)
16
+
17
+ puts ' services'.pastel(:green)
18
+ puts ' Show services status and version'
19
+ puts
20
+
21
+ puts ' status'.pastel(:green)
22
+ puts ' Show gitlab-ctl status'
23
+ puts
24
+
25
+ puts ' architecture'.pastel(:green)
26
+ puts ' Show node responsibility. E.g. Webservice, Gitaly, and etc'
27
+ puts
28
+
29
+ puts ' version'.pastel(:green)
30
+ puts ' Show GitLab software version'
31
+ puts
32
+ end
33
+
34
+ def self.architecture
35
+ results = Archive.all.map { |x| GitLab.identify_node(x) }
36
+
37
+ GitLab.node_types.each do |entry|
38
+ list = results.select { |x| (x.services & entry.pattern).any? }.map(&:host).join(', ')
39
+ next if list.blank?
40
+
41
+ puts entry.name.pastel(:bright_green)
42
+ puts list
43
+ puts
44
+ end
45
+ end
46
+
6
47
  def self.version
7
48
  Thing.where(type: 'gitlab/version-manifest.json').each do |file|
8
49
  next unless file.data
9
50
 
10
51
  puts file.friendly_name
11
- puts Semantic::Version.new(file.data.build_version).to_s.colorize(:yellow)
52
+ puts Semantic::Version.new(file.data.build_version).to_s.pastel(:yellow)
12
53
  puts
13
54
  end
14
55
  end
15
56
 
57
+ # Print service info / shared report method
58
+ def self.services(raw = {})
59
+ _files, flags, _args = Args.parse(raw, [:truncate])
60
+
61
+ results = {}
62
+
63
+ Archive.all.each do |archive|
64
+ result = GitLab.services(archive)
65
+ next unless result
66
+
67
+ # Generated Output
68
+ results[archive.friendly_name.pastel(:blue)] = result
69
+ end
70
+
71
+ # Print
72
+ ShellHelper.show(results, flags)
73
+ end
74
+
16
75
  def self.status
17
76
  Thing.where(type: 'gitlab_status').each do |file|
18
77
  next unless file.data
@@ -27,7 +86,7 @@ module GreenHat
27
86
 
28
87
  [
29
88
  "#{service.status}:",
30
- service.name.ljust(pad).colorize(color),
89
+ service.name.ljust(pad).pastel(color),
31
90
  "#{service.pid_uptime};".ljust(pad)
32
91
 
33
92
  ]
@@ -1,15 +1,98 @@
1
- # module GreenHat
2
- # module Shell
3
- # # CLI Helper
4
- # module Help
5
- # def self.help
6
- # puts 'Available Commands: '
7
- # puts '=> help'
8
-
9
- # ShellCommand.descendants_s.each do |item|
10
- # puts "-> #{item.colorize(:yellow)}"
11
- # end
12
- # end
13
- # end
14
- # end
15
- # end
1
+ module GreenHat
2
+ # Root Level Shell / Splitting Help into its own file
3
+ module Shell
4
+ # rubocop:disable Layout/LineLength
5
+ def self.help
6
+ puts 'Quickstart'.pastel(:blue)
7
+ puts " Commands are organized by submodule: #{'log'.pastel(:blue)}, #{'cat'.pastel(:blue)}, #{'faststats'.pastel(:blue)}"
8
+ puts " Use #{'help'.pastel(:bright_blue)} for available commands in each module"
9
+ puts
10
+
11
+ puts ' Example Commands'
12
+ puts ' log filter sidekiq/current'.pastel(:yellow)
13
+ puts ' disk free'.pastel(:yellow)
14
+ puts
15
+
16
+ puts 'Top Level Commands'.pastel(:blue)
17
+ puts ' report'.pastel(:green)
18
+ puts ' Show summary report of SOS Report. OS, CPU, Memory, Disk, and etc'
19
+ puts ' --raw, no pagination'
20
+ puts ' --archive=<redis/archive>, filter by archive name'
21
+ puts
22
+
23
+ puts ' ps,df,netstat,free,uptime,uname'.pastel(:green)
24
+ puts ' Show common files from archives / Emulate terminal commands'
25
+ puts
26
+
27
+ puts ' Noisy Output'.pastel(:green)
28
+ puts " Use #{'quiet'.pastel(:blue)} or to #{'debug'.pastel(:blue)} to toggle greenhat logging"
29
+ puts
30
+
31
+ cli_shortcuts
32
+
33
+ puts "See #{'about'.pastel(:bright_blue)} for more details about GreenHat"
34
+ end
35
+ # rubocop:enable Layout/LineLength
36
+
37
+ def self.cli_shortcuts
38
+ puts "\u2500".pastel(:cyan) * 25
39
+ puts 'Nav / Keyboard Shortcuts'.pastel(:blue)
40
+ puts "\u2500".pastel(:cyan) * 25
41
+ puts <<~BLOCK
42
+ | Hotkey | Description |
43
+ | ------------------- | ----------------------- |
44
+ | Ctrl + U | Clear Input |
45
+ | Ctrl + A | Go to beginning |
46
+ | Ctrl + E | Go to End |
47
+ | Ctrl + Left/Right | Move left/right by word |
48
+ | Ctrl + D, Ctrl + Z | Exit |
49
+ | Ctrl + C, Shift Tab | Up one module |
50
+ BLOCK
51
+ puts
52
+ end
53
+
54
+ def self.about
55
+ puts "\u2500".pastel(:cyan) * 20
56
+ puts "About GreenHat #{GreenHat::VERSION}".pastel(:yellow)
57
+ puts "\u2500".pastel(:cyan) * 20
58
+
59
+ puts 'TLDR; Put in SOS reports, run commands, and find stuffs'.pastel(:green)
60
+ puts
61
+
62
+ puts <<~BLOCK
63
+ General overview (OS, Memory, Disk, GitLab)
64
+ #{'report'.pastel(:bright_cyan)}
65
+
66
+ Log Searching
67
+ #{'log filter sidekiq/current --job_status=done --sort=duration_s,db_duration_s --slice=duration_s,db_duration_s --reverse'.pastel(:bright_cyan)}
68
+
69
+ Read File(s) across SOS archives
70
+ #{'cat uptime'.pastel(:bright_cyan)} or #{'cat mount etc/fstab'.pastel(:bright_cyan)}
71
+
72
+ BLOCK
73
+
74
+ puts 'What it does / How it works'.pastel(:blue)
75
+ puts
76
+ puts <<~BLOCK
77
+ GreenHat is a support utility to enhance troubleshooting with GitLabSOS Reports and log files. Make it easy to find stuff
78
+
79
+ Supplied input files are staged, unpacked, identified, and normalized.
80
+ This enables other utilities to automatically find and present data. (Faststats, report, and etc)
81
+
82
+ BLOCK
83
+
84
+ puts 'Commands and Submodules'.pastel(:blue)
85
+ puts
86
+ puts <<~BLOCK
87
+ Greenhat is broken down into different "modules". Each module has its own commands. For example: log, cat, and faststats.
88
+ You can "cd" into or execute commands directly against with their names.
89
+
90
+ - Direct: #{'log filter sidekiq/current'.pastel(:cyan)}
91
+ - Or within: First #{'log'.pastel(:cyan)}, then #{'filter sidekiq/current'.pastel(:cyan)}
92
+
93
+ You can find the list of commands and submodules of each with #{'help'.pastel(:yellow)}
94
+
95
+ BLOCK
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,46 @@
1
+ module GreenHat
2
+ module ShellHelper
3
+ # Helper to handle listing of files
4
+ module List
5
+ # List Files Helpers
6
+ def self.list(raw = [], files)
7
+ filter, flags, _args = Args.parse(raw)
8
+
9
+ # Sort
10
+ files.sort_by!(&:name)
11
+
12
+ # Simplified vs Full. Full file name/path / or just file kinds
13
+ all = flags.key?(:all) || flags.key?(:a)
14
+
15
+ # Short & Uniq
16
+ files.uniq!(&:name) unless all
17
+
18
+ # Filter / Pattern
19
+ files.select! { |f| filter.any? { |x| f.name.include? x } } unless filter.empty?
20
+
21
+ # Print
22
+ files.each do |log|
23
+ if all
24
+ puts "- #{log.friendly_name}"
25
+ else
26
+ puts "- #{log.name.pastel(:yellow)}"
27
+ end
28
+ end
29
+ end
30
+
31
+ # Unified Help
32
+ def self.help
33
+ puts ' ls'.pastel(:green)
34
+ puts ' List available files'
35
+ puts ' Options'.pastel(:cyan)
36
+ puts ' -a, --all, show full file name/path including source'
37
+ puts ' <string> filter available'
38
+ puts ' Examples'.pastel(:cyan)
39
+ puts ' ls -a rails'
40
+ puts ' ls sys'
41
+ puts
42
+ end
43
+ # ----
44
+ end
45
+ end
46
+ end