greenhat 0.3.5 → 0.3.6

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/lib/greenhat/accessors/disk.rb +1 -3
  3. data/lib/greenhat/accessors/gitlab.rb +5 -2
  4. data/lib/greenhat/archive.rb +7 -2
  5. data/lib/greenhat/cli.rb +36 -0
  6. data/lib/greenhat/host.rb +25 -37
  7. data/lib/greenhat/shell/args.rb +22 -9
  8. data/lib/greenhat/shell/faststats.rb +23 -3
  9. data/lib/greenhat/shell/field_helper.rb +1 -1
  10. data/lib/greenhat/shell/filter_help.rb +32 -10
  11. data/lib/greenhat/shell/log.rb +142 -7
  12. data/lib/greenhat/shell/markdown.rb +16 -8
  13. data/lib/greenhat/shell/old_search_helper.rb +54 -0
  14. data/lib/greenhat/shell/page.rb +1 -1
  15. data/lib/greenhat/shell/pipe.rb +31 -0
  16. data/lib/greenhat/shell/platform.rb +28 -0
  17. data/lib/greenhat/shell/report.rb +47 -15
  18. data/lib/greenhat/shell/shell_helper.rb +92 -101
  19. data/lib/greenhat/shell.rb +7 -0
  20. data/lib/greenhat/thing/file_types.rb +28 -0
  21. data/lib/greenhat/thing/formatters/json.rb +4 -0
  22. data/lib/greenhat/thing/formatters/nginx.rb +6 -2
  23. data/lib/greenhat/thing/formatters/time_space.rb +0 -16
  24. data/lib/greenhat/thing/helpers.rb +12 -0
  25. data/lib/greenhat/thing.rb +10 -0
  26. data/lib/greenhat/version.rb +1 -1
  27. data/lib/greenhat/views/api.slim +55 -0
  28. data/lib/greenhat/views/chart.slim +42 -0
  29. data/lib/greenhat/views/chart_template.slim +31 -0
  30. data/lib/greenhat/views/chartkick.js +21 -0
  31. data/lib/greenhat/views/css.slim +47 -0
  32. data/lib/greenhat/views/gitaly.slim +53 -0
  33. data/lib/greenhat/views/headers.slim +16 -0
  34. data/lib/greenhat/views/index-old.slim +51 -0
  35. data/lib/greenhat/views/index.slim +14 -14
  36. data/lib/greenhat/views/info.slim +17 -18
  37. data/lib/greenhat/views/production.slim +55 -0
  38. data/lib/greenhat/views/sidekiq.slim +55 -0
  39. data/lib/greenhat/views/time.slim +63 -0
  40. data/lib/greenhat/views/workhorse.slim +16 -0
  41. data/lib/greenhat/web/api.rb +94 -0
  42. data/lib/greenhat/web/chartkick_shim.rb +14 -0
  43. data/lib/greenhat/web/faststats.rb +44 -0
  44. data/lib/greenhat/web/gitaly.rb +65 -0
  45. data/lib/greenhat/web/helpers.rb +198 -0
  46. data/lib/greenhat/web/production.rb +104 -0
  47. data/lib/greenhat/web/sidekiq.rb +73 -0
  48. data/lib/greenhat/web/stats_helpers.rb +74 -0
  49. data/lib/greenhat/web/time.rb +36 -0
  50. data/lib/greenhat/web/workhorse.rb +43 -0
  51. data/lib/greenhat/web.rb +63 -19
  52. data/lib/greenhat.rb +1 -0
  53. metadata +68 -2
@@ -2,6 +2,7 @@ module GreenHat
2
2
  # CLI Helper
3
3
  module Shell
4
4
  # Logs
5
+ # rubocop:disable Metrics/ModuleLength
5
6
  module Log
6
7
  def self.auto_complete(list, word)
7
8
  # Argument Parsing
@@ -58,6 +59,18 @@ module GreenHat
58
59
  puts " General full text by file searching. See #{'search_help'.pastel(:blue)}"
59
60
  puts
60
61
 
62
+ puts ' save'.pastel(:green)
63
+ puts ' Save the last query result into a new searchable object'
64
+ puts
65
+
66
+ puts ' write'.pastel(:green)
67
+ puts ' Write the last query result into a local file'
68
+ puts
69
+
70
+ puts ' visualize'.pastel(:green)
71
+ puts ' Load web services and formulate last query for the UI'
72
+ puts
73
+
61
74
  puts ShellHelper::List.help
62
75
 
63
76
  puts "See #{'examples'.pastel(:bright_blue)} for query examples"
@@ -89,6 +102,95 @@ module GreenHat
89
102
  ShellHelper.show files.map(&:data).flatten
90
103
  end
91
104
 
105
+ def self.save(raw = [])
106
+ if ShellHelper::Log.last.nil?
107
+ puts 'No previous query found'.pastel(:red)
108
+ puts 'Run a query first then save to store as a new log'
109
+ puts
110
+ puts "Try #{'nginx/gitlab_access.log --status!=200'.pastel(:green)} then #{'save'.pastel(:green)}"
111
+ true
112
+ end
113
+
114
+ name = if raw.empty?
115
+ Cli.prompt.ask('Log/name to save the results to? '.pastel(:yellow))
116
+ else
117
+ raw.first
118
+ end
119
+
120
+ if name.blank?
121
+ puts 'Name required'.pastel(:red)
122
+ return true
123
+ end
124
+
125
+ results = ShellHelper.filter_internal ShellHelper::Log.last
126
+
127
+ # Don't save empty results
128
+ if results.empty?
129
+ puts 'No results'.pastel(:red)
130
+ ShellHelper::Log.no_files_warning(files) if ShellHelper.find_things(files, flags).count.zero?
131
+ return false
132
+ end
133
+
134
+ Thing.new.query_save(results, name)
135
+ puts "#{name.pastel(:green)} Saved!"
136
+ end
137
+
138
+ def self.write(raw = [])
139
+ if ShellHelper::Log.last.nil?
140
+ puts 'No previous query found'.pastel(:red)
141
+ puts 'Run a query first then write'
142
+ puts
143
+ puts "Try #{'nginx/gitlab_access.log --status!=200'.pastel(:green)} then #{'save'.pastel(:green)}"
144
+ true
145
+ end
146
+
147
+ name = if raw.empty?
148
+ Cli.prompt.ask('Log/name to save the results to? '.pastel(:yellow))
149
+ else
150
+ raw.first
151
+ end
152
+
153
+ if name.blank?
154
+ puts 'Name required'.pastel(:red)
155
+ return true
156
+ end
157
+
158
+ results = ShellHelper.filter_internal ShellHelper::Log.last
159
+
160
+ # Don't save empty results
161
+ if results.empty?
162
+ puts 'No results'.pastel(:red)
163
+ ShellHelper::Log.no_files_warning(files) if ShellHelper.find_things(files, flags).count.zero?
164
+ return false
165
+ end
166
+
167
+ all = results.map { |row| Oj.dump(row) }
168
+ File.write(name, all.join("\n"))
169
+ puts "#{name.pastel(:green)} File Written!"
170
+ end
171
+
172
+ def self.visualize
173
+ if ShellHelper::Log.last.nil?
174
+ puts 'No previous query found'.pastel(:red)
175
+ puts 'Run a query first then visualize to load it in the chart web page'
176
+ puts
177
+ puts "Try #{'nginx/gitlab_access.log --status!=200'.pastel(:green)} then #{'visualize'.pastel(:green)}"
178
+ return true
179
+ end
180
+
181
+ # Load Required Files
182
+ require 'greenhat/web'
183
+
184
+ unless GreenHat::Web.alive?
185
+ GreenHat::Web.start
186
+ sleep 0.2
187
+ end
188
+
189
+ url = "http://localhost:4567/chart/time?query=#{CGI.escape(ShellHelper::Log.last)}"
190
+
191
+ GreenHat::Platform.open url
192
+ end
193
+
92
194
  # ========================================================================
93
195
  # Filter (See Filter Help)
94
196
  # ========================================================================
@@ -103,6 +205,8 @@ module GreenHat
103
205
  return true
104
206
  end
105
207
 
208
+ ShellHelper::Log.last = raw
209
+
106
210
  # Argument Parsing
107
211
  files, flags, args = Args.parse(raw)
108
212
 
@@ -113,7 +217,7 @@ module GreenHat
113
217
 
114
218
  # Skip and Print Total if set
115
219
  if flags[:total]
116
- ShellHelper.total_count(results)
220
+ ShellHelper.total_count(results, flags)
117
221
  return true
118
222
  end
119
223
 
@@ -126,13 +230,13 @@ module GreenHat
126
230
  # Check Search Results
127
231
  if results.instance_of?(Hash) && results.values.flatten.empty?
128
232
  puts 'No results'.pastel(:red)
233
+ ShellHelper::Log.no_files_warning(files) if ShellHelper.find_things(files, flags).count.zero?
234
+ elsif flags[:pipe]
235
+ Pipe.show(results, flags[:pipe])
129
236
  else
130
237
  # This causes the key 'colorized' output to also be included
131
238
  ShellHelper.show(results.to_a.compact.flatten, flags)
132
239
  end
133
-
134
- # log filter --path='cloud/gitlab-automation' --path='/pull' --all
135
- # log filter --project=thingy --other_filter=asdf *
136
240
  rescue StandardError => e
137
241
  LogBot.fatal('Filter', message: e.message)
138
242
  ap e.backtrace
@@ -161,9 +265,25 @@ module GreenHat
161
265
  puts 'Count/% occurences for both user and remote ip fields'.pastel(:bright_green)
162
266
  puts 'gitlab-rails/api_json.log --stats=meta.user,meta.remote_ip --exists=meta.user'
163
267
  puts
164
- end
165
268
 
269
+ puts 'Sidekiq jobs that took over 5 seconds excluding LdapSyncWorker jobs'.pastel(:bright_green)
270
+ puts 'sidekiq/current --duration_s>=5 --class!=LdapSyncWorker'
271
+ puts
272
+
273
+ puts 'Search access logs for runner requests, exclude specific runner version'.pastel(:bright_green)
274
+ puts 'nginx/gitlab_access.log --http_user_agent=gitlab-runner --http_user_agent!=13.12.0'
275
+ puts
276
+
277
+ puts 'Get a list of unique Gitaly error messages for a specific project'.pastel(:bright_green)
278
+ puts 'filter --level=error --grpc.request.glProjectPath=path/to/project gitaly/current --slice=error --uniq=error'
279
+ puts
280
+
281
+ puts 'Show workhorse duration/URI. Filter by duration bounds'.pastel(:bright_green)
282
+ puts 'gitlab-workhorse/current --duration_ms>=30000 --duration_ms<=45000 --slice=duration_ms,uri'
283
+ puts
284
+ end
166
285
  # rubocop:enable Layout/LineLength
286
+
167
287
  # ========================================================================
168
288
  # Search (Full Text / String Search)
169
289
  # ========================================================================
@@ -178,13 +298,15 @@ module GreenHat
178
298
 
179
299
  # Skip and Print Total if set
180
300
  if flags[:total]
181
- ShellHelper.total_count(results)
301
+ ShellHelper.total_count(results, flags)
182
302
  return true
183
303
  end
184
304
 
185
305
  # Check Search Results
186
306
  if results.values.flatten.empty?
187
307
  puts 'No results'.pastel(:red)
308
+ ShellHelper::Log.no_files_warning(files) if ShellHelper.find_things(files, flags).count.zero?
309
+
188
310
  else
189
311
  # This causes the key 'colorized' output to also be included
190
312
  ShellHelper.show(results.to_a.compact.flatten, flags)
@@ -249,7 +371,7 @@ module GreenHat
249
371
  puts 'log search --text=BuildHooksWorker --text!=start --slice=enqueued_at sidekiq/current'
250
372
  puts
251
373
  end
252
- # rubocop:enable Metrics/MethodLength
374
+ # rubocop:enable Metrics/MethodLength,Metrics/ModuleLength
253
375
 
254
376
  # ------------------------------------------------------------------------
255
377
  end
@@ -260,9 +382,22 @@ module GreenHat
260
382
  module ShellHelper
261
383
  # Log Helpers
262
384
  module Log
385
+ def self.last=(value)
386
+ @last = value.join(' ')
387
+ end
388
+
389
+ def self.last
390
+ @last
391
+ end
392
+
263
393
  def self.list
264
394
  Thing.all.select(&:log)
265
395
  end
396
+
397
+ def self.no_files_warning(files)
398
+ puts "No matching files found for pattern #{files.to_s.pastel(:yellow)}"
399
+ puts "See #{'ls'.pastel(:blue)} for available files"
400
+ end
266
401
  end
267
402
  # --------
268
403
  end
@@ -58,6 +58,9 @@ module GreenHat
58
58
  ''
59
59
  ]
60
60
 
61
+ # GitLab Version
62
+ output << "**GitLab #{gitlab_version}**\n" if gitlab_manifest || gitlab_status
63
+
61
64
  # OS
62
65
  output << "**OS**\n"
63
66
 
@@ -79,8 +82,6 @@ module GreenHat
79
82
  end
80
83
 
81
84
  # Gitlab
82
- output << "**GitLab**\n" if gitlab_manifest || gitlab_status
83
- output << gitlab_version if gitlab_manifest
84
85
  output << gitlab_services if gitlab_status
85
86
 
86
87
  output << ''
@@ -172,7 +173,8 @@ module GreenHat
172
173
  results = ShellHelper.filter_internal([
173
174
  'gitlab-rails/application_json.log',
174
175
  '--message!="Cannot obtain an exclusive lease"',
175
- '--severity=error'
176
+ '--severity=error',
177
+ "--archive=#{archive.name}"
176
178
  ].join(' '))
177
179
 
178
180
  "Application: #{results.count}"
@@ -195,7 +197,13 @@ module GreenHat
195
197
  end
196
198
 
197
199
  def gitlab_version
198
- "Version: #{gitlab_manifest.data.build_version}\n"
200
+ txt = gitlab_manifest.data.dig(:software, :'gitlab-rails', :display_version) || gitlab_manifest.data.build_version
201
+
202
+ if txt.include? '-ce'
203
+ txt += ' - [😱 CE](https://about.gitlab.com/support/statement-of-support.html#free-and-community-edition-users)!'
204
+ end
205
+
206
+ "Version: #{txt}"
199
207
  end
200
208
 
201
209
  def hostname
@@ -295,10 +303,10 @@ module GreenHat
295
303
 
296
304
  pad = 6
297
305
  list = [
298
- "#{title('Total', pad)} #{number_to_human_size(free.total.to_i * 1024**2)}",
299
- "#{title('Used', pad)} #{number_to_human_size(free.used.to_i * 1024**2)}",
300
- "#{title('Free', pad)} #{number_to_human_size(free.free.to_i * 1024**2)}",
301
- "#{title('Avail', pad)} #{number_to_human_size(free.available.to_i * 1024**2)}"
306
+ "#{title('Total', pad)} #{number_to_human_size(free.total.to_i * (1024**2))}",
307
+ "#{title('Used', pad)} #{number_to_human_size(free.used.to_i * (1024**2))}",
308
+ "#{title('Free', pad)} #{number_to_human_size(free.free.to_i * (1024**2))}",
309
+ "#{title('Avail', pad)} #{number_to_human_size(free.available.to_i * (1024**2))}"
302
310
  ]
303
311
 
304
312
  # Keep Alphabetical Sort
@@ -0,0 +1,54 @@
1
+ module GreenHat
2
+ # Deprecating search stuff
3
+ module ShellHelper
4
+ # Main Entry Point for Searching
5
+ # def self.search_start(log_list, filter_type, args, opts)
6
+ def self.search_start(files, flags, args)
7
+ # Convert to Things
8
+ logs = ShellHelper.find_things(files, flags)
9
+
10
+ logs.each_with_object({}) do |log, obj|
11
+ # Ignore Empty Results / No Thing
12
+ next if log&.data.blank?
13
+
14
+ obj[log.friendly_name] = ShellHelper.search(log.data, flags, args)
15
+
16
+ obj
17
+ end
18
+ end
19
+
20
+ # Generic Search Helper / String/Regex
21
+ def self.search(data, flags = {}, args = {})
22
+ results = data.clone.flatten.compact
23
+ results.select! do |row|
24
+ args.send(flags.logic) do |arg|
25
+ search_row(row, arg, flags)
26
+ end
27
+ end
28
+
29
+ # Strip Results if Slice is defined
30
+ results.map! { |row| row.slice(*flags[:slice]) } if flags[:slice]
31
+
32
+ # Strip Results if Except is defined
33
+ results.map! { |row| row.except(*flags[:except]) } if flags[:except]
34
+
35
+ # Remove Blank from either slice or except
36
+ results.reject!(&:empty?)
37
+
38
+ results
39
+ end
40
+
41
+ # Break out filter row logic into separate method
42
+ def self.search_row(row, arg, flags)
43
+ # Sensitivity Check / Check for Match
44
+ included = filter_row_entry(row.to_s, arg, flags)
45
+
46
+ # Pivot of off include vs exclude
47
+ if arg.bang
48
+ !included
49
+ else
50
+ included
51
+ end
52
+ end
53
+ end
54
+ end
@@ -8,7 +8,7 @@ module GreenHat
8
8
  # Pass if Explicitly Set / Inverse for skip
9
9
  return !flags[:page] if flags.key? :page
10
10
 
11
- LogBot.debug('Page', count_rows(data, flags)) if ENV['DEBUG']
11
+ LogBot.debug('Page Skip', count_rows(data, flags)) if ENV['DEBUG']
12
12
 
13
13
  count_rows(data, flags)
14
14
  end
@@ -0,0 +1,31 @@
1
+ module GreenHat
2
+ # Helper for piping arguments
3
+ module Pipe
4
+ def self.show(results, arg)
5
+ uuid = SecureRandom.uuid # File Placeholder
6
+ write(results, uuid)
7
+ command(uuid, arg)
8
+ delete(uuid)
9
+ end
10
+
11
+ # Final execution into pipe
12
+ def self.command(uuid, arg)
13
+ puts `cat "#{file(uuid)}" | #{arg}`
14
+ end
15
+
16
+ # File path helper
17
+ def self.file(uuid)
18
+ "#{$TMP}/#{uuid}.txt"
19
+ end
20
+
21
+ # Helper to write all into split lines for pipe
22
+ def self.write(results, uuid)
23
+ File.write(file(uuid), results.map(&:flatten).flatten.join("\n"))
24
+ end
25
+
26
+ # Clean up created file
27
+ def self.delete(uuid)
28
+ File.delete file(uuid)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ module GreenHat
2
+ # Common Helpers
3
+ module Platform
4
+ def self.platform
5
+ if @platform
6
+ @platform
7
+ else
8
+ require 'tty-platform'
9
+ @platform ||= TTY::Platform.new
10
+
11
+ end
12
+ end
13
+
14
+ def self.open(url = 'http://localhost:4567/chart/time')
15
+ cmd = if platform.linux? || platform.unix?
16
+ 'xdg-open'
17
+ elsif platform.mac?
18
+ 'open'
19
+ end
20
+
21
+ # platform.windows? # => false
22
+ # platform.unix? # => true
23
+ # platform.linux? # => false
24
+ # platform.mac? # => true
25
+ system("#{cmd} '#{url}'")
26
+ end
27
+ end
28
+ end
@@ -12,7 +12,11 @@ module GreenHat
12
12
  Archive.all
13
13
  end
14
14
 
15
- ShellHelper.show(archives.map(&:report).map(&:show).flatten, flags)
15
+ output = archives.map { |x| x.report(flags) }.map(&:show).flatten
16
+
17
+ flags[:page] = true if flags.full && !flags.raw
18
+
19
+ ShellHelper.show(output, flags)
16
20
  end
17
21
  end
18
22
  end
@@ -23,15 +27,16 @@ module GreenHat
23
27
  class Report
24
28
  include ActionView::Helpers::NumberHelper
25
29
 
26
- attr_accessor :archive, :host, :os_release, :selinux_status, :cpu, :uname,
30
+ attr_accessor :archive, :flags, :host, :os_release, :selinux_status, :cpu, :uname,
27
31
  :timedatectl, :uptime, :meminfo, :gitlab_manifest, :gitlab_status,
28
- :production_log, :api_log, :application_log, :sidekiq_log,
32
+ :production_log, :api_log, :sidekiq_log,
29
33
  :exceptions_log, :gitaly_log, :free_m, :disk_free
30
34
 
31
35
  # Find Needed Files for Report
32
36
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
33
- def initialize(archive)
37
+ def initialize(archive, flags)
34
38
  self.archive = archive
39
+ self.flags = flags
35
40
  self.host = archive.things.find { |x| x.name == 'hostname' }
36
41
  self.os_release = archive.things.find { |x| x.name == 'etc/os-release' }
37
42
  self.selinux_status = archive.things.find { |x| x.name == 'sestatus' }
@@ -45,7 +50,6 @@ module GreenHat
45
50
  self.gitlab_status = archive.things.find { |x| x.name == 'gitlab_status' }
46
51
  self.production_log = archive.things.find { |x| x.name == 'gitlab-rails/production_json.log' }
47
52
  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
53
  self.exceptions_log = archive.things.find { |x| x.name == 'gitlab-rails/exceptions_json.log' }
50
54
  self.gitaly_log = archive.things.find { |x| x.name == 'gitaly/current' }
51
55
  self.sidekiq_log = archive.things.find { |x| x.name == 'sidekiq/current' }
@@ -87,13 +91,16 @@ module GreenHat
87
91
  output << 'GitLab'.pastel(:bright_yellow) if gitlab_manifest
88
92
  output << gitlab_version if gitlab_manifest
89
93
  output << gitlab_services if gitlab_status
90
- output << title('Errors') if production_log || api_log || application_log || sidekiq_log
94
+ output << title('Errors') if production_log || api_log || sidekiq_log
91
95
  output << production_errors if production_log
92
- output << application_errors if application_log
96
+ output << application_errors if archive.thing?('gitlab-rails/application_json.log')
93
97
  output << sidekiq_errors if sidekiq_log
94
98
  output << api_errors if api_log
95
99
  output << exception_errors if exceptions_log
96
100
  output << gitaly_errors if gitaly_log
101
+ output << workhorse_errors if archive.thing?('gitlab-workhorse/current')
102
+
103
+ full(output) if flags.full
97
104
 
98
105
  # Final Space / Return
99
106
  output << ''
@@ -101,6 +108,17 @@ module GreenHat
101
108
  end
102
109
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
103
110
 
111
+ def full(output)
112
+ output << ''
113
+ output << 'FastStats Top'
114
+ Shell::Faststats.top(['--raw'], true).each { |x| output << x } # Page Row Helper
115
+ output << ''
116
+
117
+ output << 'FastStats Errors'
118
+ Shell::Faststats.errors(['--raw'], true).each { |x| output << x } # Page Row Helper
119
+ output << ''
120
+ end
121
+
104
122
  def exception_errors
105
123
  count = exceptions_log.data.count
106
124
  color = count.zero? ? :green : :red
@@ -111,6 +129,21 @@ module GreenHat
111
129
  ].join
112
130
  end
113
131
 
132
+ def workhorse_errors
133
+ results = ShellHelper.filter_internal([
134
+ 'gitlab-workhorse/current',
135
+ '--level=error',
136
+ "--archive=#{archive.name}"
137
+ ].join(' '))
138
+
139
+ color = results.count.zero? ? :green : :red
140
+
141
+ [
142
+ title(' Workhorse', :bright_red, 18),
143
+ results.count.to_s.pastel(color)
144
+ ].join
145
+ end
146
+
114
147
  def gitaly_errors
115
148
  count = gitaly_log.data.count { |x| x.level == 'error' }
116
149
  color = count.zero? ? :green : :red
@@ -145,7 +178,8 @@ module GreenHat
145
178
  results = ShellHelper.filter_internal([
146
179
  'gitlab-rails/application_json.log',
147
180
  '--message!="Cannot obtain an exclusive lease"',
148
- '--severity=error'
181
+ '--severity=error',
182
+ "--archive=#{archive.name}"
149
183
  ].join(' '))
150
184
 
151
185
  count = results.count { |x| x&.severity == 'ERROR' }
@@ -300,13 +334,12 @@ module GreenHat
300
334
  title('Usage'),
301
335
  ' ['.pastel(:bright_black),
302
336
  '='.pastel(:green) * (used / 2),
303
- ' ' * (50 - used / 2),
337
+ ' ' * (50 - (used / 2)),
304
338
  ']'.pastel(:bright_black),
305
339
  " #{100 - percent(free, total)}%".pastel(:green) # Inverse
306
340
  ].join
307
341
  end
308
342
 
309
- # rubocop:disable Metrics/MethodLength
310
343
  def memory_free
311
344
  free = free_m.data.find { |x| x.kind == 'Mem' }
312
345
 
@@ -317,25 +350,25 @@ module GreenHat
317
350
  output = []
318
351
  unless free.total.blank?
319
352
  output << title('Total', :cyan, 14)
320
- output << number_to_human_size(free.total.to_i * 1024**2)
353
+ output << number_to_human_size(free.total.to_i * (1024**2))
321
354
  output << "\n"
322
355
  end
323
356
 
324
357
  unless free.total.blank?
325
358
  output << title('Used', :yellow, 14)
326
- output << number_to_human_size(free.used.to_i * 1024**2)
359
+ output << number_to_human_size(free.used.to_i * (1024**2))
327
360
  output << "\n"
328
361
  end
329
362
 
330
363
  unless free.total.blank?
331
364
  output << title('Free', :blue, 14)
332
- output << number_to_human_size(free.free.to_i * 1024**2)
365
+ output << number_to_human_size(free.free.to_i * (1024**2))
333
366
  output << "\n"
334
367
  end
335
368
 
336
369
  unless free.total.blank?
337
370
  output << title('Available', :green, 14)
338
- output << number_to_human_size(free.available.to_i * 1024**2)
371
+ output << number_to_human_size(free.available.to_i * (1024**2))
339
372
  output << "\n"
340
373
  end
341
374
 
@@ -346,7 +379,6 @@ module GreenHat
346
379
  rescue StandardError => e
347
380
  LogBot.fatal('Memory', message: e.message, backtrace: e.backtrace.first)
348
381
  end
349
- # rubocop:enable Metrics/MethodLength
350
382
 
351
383
  def disks
352
384
  # GreenHat::Disk.df({archive: []})