greenhat 0.3.3 → 0.4.0

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/bin/greenhat +1 -4
  3. data/lib/greenhat/accessors/disk.rb +42 -3
  4. data/lib/greenhat/accessors/gitlab.rb +30 -1
  5. data/lib/greenhat/accessors/memory.rb +1 -1
  6. data/lib/greenhat/archive.rb +19 -8
  7. data/lib/greenhat/cli.rb +24 -126
  8. data/lib/greenhat/entrypoint.rb +170 -0
  9. data/lib/greenhat/host.rb +25 -37
  10. data/lib/greenhat/settings.rb +6 -0
  11. data/lib/greenhat/shell/args.rb +22 -9
  12. data/lib/greenhat/shell/faststats.rb +23 -3
  13. data/lib/greenhat/shell/field_helper.rb +1 -1
  14. data/lib/greenhat/shell/filter_help.rb +232 -9
  15. data/lib/greenhat/shell/log.rb +153 -9
  16. data/lib/greenhat/shell/markdown.rb +352 -0
  17. data/lib/greenhat/shell/old_search_helper.rb +54 -0
  18. data/lib/greenhat/shell/page.rb +1 -1
  19. data/lib/greenhat/shell/pipe.rb +31 -0
  20. data/lib/greenhat/shell/platform.rb +28 -0
  21. data/lib/greenhat/shell/report.rb +106 -25
  22. data/lib/greenhat/shell/shell_helper.rb +115 -106
  23. data/lib/greenhat/shell.rb +10 -3
  24. data/lib/greenhat/thing/file_types.rb +136 -8
  25. data/lib/greenhat/thing/formatters/json.rb +4 -0
  26. data/lib/greenhat/thing/formatters/kube_json.rb +36 -0
  27. data/lib/greenhat/thing/formatters/kube_nginx.rb +48 -0
  28. data/lib/greenhat/thing/formatters/kube_webservice.rb +51 -0
  29. data/lib/greenhat/thing/formatters/nginx.rb +6 -2
  30. data/lib/greenhat/thing/formatters/registry.rb +47 -0
  31. data/lib/greenhat/thing/formatters/time_space.rb +0 -16
  32. data/lib/greenhat/thing/helpers.rb +12 -0
  33. data/lib/greenhat/thing/kind.rb +5 -0
  34. data/lib/greenhat/thing.rb +11 -0
  35. data/lib/greenhat/version.rb +1 -1
  36. data/lib/greenhat/views/api.slim +55 -0
  37. data/lib/greenhat/views/chart.slim +42 -0
  38. data/lib/greenhat/views/chart_template.slim +31 -0
  39. data/lib/greenhat/views/chartkick.js +21 -0
  40. data/lib/greenhat/views/css.slim +47 -0
  41. data/lib/greenhat/views/gitaly.slim +53 -0
  42. data/lib/greenhat/views/headers.slim +16 -0
  43. data/lib/greenhat/views/index-old.slim +51 -0
  44. data/lib/greenhat/views/index.slim +14 -14
  45. data/lib/greenhat/views/info.slim +17 -18
  46. data/lib/greenhat/views/production.slim +55 -0
  47. data/lib/greenhat/views/sidekiq.slim +55 -0
  48. data/lib/greenhat/views/time.slim +63 -0
  49. data/lib/greenhat/views/workhorse.slim +16 -0
  50. data/lib/greenhat/web/api.rb +94 -0
  51. data/lib/greenhat/web/chartkick_shim.rb +14 -0
  52. data/lib/greenhat/web/faststats.rb +44 -0
  53. data/lib/greenhat/web/gitaly.rb +65 -0
  54. data/lib/greenhat/web/helpers.rb +198 -0
  55. data/lib/greenhat/web/production.rb +104 -0
  56. data/lib/greenhat/web/sidekiq.rb +73 -0
  57. data/lib/greenhat/web/stats_helpers.rb +74 -0
  58. data/lib/greenhat/web/time.rb +36 -0
  59. data/lib/greenhat/web/workhorse.rb +43 -0
  60. data/lib/greenhat/web.rb +63 -19
  61. data/lib/greenhat.rb +2 -0
  62. metadata +78 -5
@@ -0,0 +1,65 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ # ======================================================
4
+ # == [ Gitaly ]
5
+ # ======================================================
6
+ def gitaly_avg_method_series
7
+ query = [
8
+ 'gitaly/current',
9
+ '--slice=time,grpc.time_ms,grpc.method',
10
+ '--exists=grpc.time_ms',
11
+ '--exists=time',
12
+ '--grpc.time_ms>=1'
13
+ ].join(' ')
14
+
15
+ results = GreenHat::ShellHelper.filter_internal(query)
16
+
17
+ method_key = :'grpc.method'
18
+ duration_key = :'grpc.time_ms'
19
+
20
+ # Collect Total by Duration
21
+ top = results.each_with_object({}) do |v, obj|
22
+ obj[v[method_key]] ||= []
23
+ obj[v[method_key]] << v[duration_key]
24
+
25
+ obj
26
+ end
27
+
28
+ # Average / Round
29
+ top.transform_values! { |x| (x.sum / x.size.to_f).round(1) }
30
+
31
+ # Only top by duration
32
+ method_calls = top.sort_by(&:last)[-10..].map(&:first)
33
+ results.select! { |x| method_calls.any? x[method_key] }
34
+
35
+ build_group_time_avg(results, method_key, duration_key) # 5 Minutes
36
+ end
37
+
38
+ def gitaly_errors_series
39
+ query = [
40
+ 'gitaly/current',
41
+ '--slice=time,grpc.method,level',
42
+ '--exists=time,level,time',
43
+ '--level=error'
44
+ ].join(' ')
45
+
46
+ results = GreenHat::ShellHelper.filter_internal(query)
47
+
48
+ build_group_time_total(results, :'grpc.method')
49
+ end
50
+
51
+ def gitaly_duration
52
+ query = [
53
+ 'gitaly/current',
54
+ '--slice=time,grpc.time_ms',
55
+ '--exists=grpc.time_ms',
56
+ '--exists=time',
57
+ '--grpc.time_ms!=0',
58
+ '--exact'
59
+ ].join(' ')
60
+
61
+ results = GreenHat::ShellHelper.filter_internal(query)
62
+
63
+ build_percentile_list(results, :'grpc.time_ms')
64
+ end
65
+ end
@@ -0,0 +1,198 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ def arg_parse
4
+ @files, @flags, @args = GreenHat::Args.parse Shellwords.split(params[:query])
5
+ end
6
+
7
+ def percent(value, total)
8
+ ((value.to_i / total.to_f) * 100).round(2)
9
+ end
10
+
11
+ def build_time_index(results, interval = 5.minutes)
12
+ index = {}
13
+ start = results.min_by(&:time).time.floor(interval)
14
+ finish = results.max_by(&:time).time.floor(interval)
15
+
16
+ loop do
17
+ index[start] = 0
18
+ start += interval
19
+ break if start > finish
20
+ end
21
+
22
+ index
23
+ rescue StandardError => e
24
+ LogBot.fatal('Index Error', e.message)
25
+ ensure
26
+ {}
27
+ end
28
+
29
+ # Without Grouping Just total count
30
+ def build_time_total(results, interval = 5.minutes)
31
+ index = build_time_index(results, interval)
32
+
33
+ results.each do |r|
34
+ index[r.time.floor(interval)] += 1
35
+ end
36
+
37
+ index
38
+ end
39
+
40
+ # Without Grouping Avg
41
+ def build_time_avg(results, sum, interval = 5.minutes)
42
+ index = build_time_index(results, interval)
43
+
44
+ index.transform_values! { |_y| [] }
45
+
46
+ results.each do |r|
47
+ index[r.time.floor(interval)] << r[sum]
48
+ end
49
+
50
+ index.transform_values! do |l|
51
+ (l.sum / l.size.to_f)
52
+ end
53
+
54
+ index
55
+ end
56
+
57
+ # by Occurance/Count / Key to String
58
+ def build_group_time_total(results, group, interval = 5.minutes)
59
+ default_index = build_time_index(results, interval)
60
+ list = {}
61
+
62
+ results.map { |x| x[group] }.uniq.each do |key|
63
+ key = 'None' if key.nil?
64
+ list[key.to_s] = {
65
+ name: key.to_s,
66
+ data: default_index.clone
67
+ }
68
+ end
69
+
70
+ results.each do |r|
71
+ key = r[group].nil? ? 'None' : r[group]
72
+ list[key.to_s].data[r.time.floor(interval)] += 1
73
+ end
74
+
75
+ list.values
76
+ end
77
+
78
+ # by Sum
79
+ def build_group_time_sum(results, group, sum, interval = 5.minutes)
80
+ default_index = build_time_index(results, interval)
81
+ list = {}
82
+
83
+ results.map { |x| x[group] }.uniq.each do |key|
84
+ key = 'None' if key.nil?
85
+ list[key] = {
86
+ name: key,
87
+ data: default_index.clone.transform_values { |_y| [] }
88
+ }
89
+ end
90
+
91
+ results.each do |r|
92
+ key = r[group].nil? ? 'None' : r[group]
93
+ list[key].data[r.time.floor(interval)] << r[sum]
94
+ end
95
+
96
+ # Transform / Calculate
97
+ list.each do |_k, v|
98
+ v.data.transform_values! do |l|
99
+ l.empty? ? 0 : l.sum
100
+ end
101
+ end
102
+
103
+ list.values
104
+ end
105
+
106
+ # by Avg
107
+ def build_group_time_avg(results, group, sum, interval = 5.minutes)
108
+ default_index = build_time_index(results, interval)
109
+ list = {}
110
+
111
+ results.map { |x| x[group] }.uniq.each do |key|
112
+ key = 'None' if key.nil?
113
+ list[key] = {
114
+ name: key,
115
+ data: default_index.clone.transform_values { |_y| [] }
116
+ }
117
+ end
118
+
119
+ results.each do |r|
120
+ key = r[group].nil? ? 'None' : r[group]
121
+ list[key].data[r.time.floor(interval)] << r[sum]
122
+ end
123
+
124
+ # Transform / Calculate
125
+ list.each do |_k, v|
126
+ v.data.transform_values! do |l|
127
+ l.empty? ? 0 : (l.sum / l.size.to_f) # .round(1)
128
+ end
129
+ end
130
+
131
+ list.values
132
+ end
133
+
134
+ # Stack AVG Helper
135
+ def build_stack_time_avg(results, stacks, interval = 5.minutes)
136
+ default_index = build_time_index(results, interval)
137
+ list = {}
138
+
139
+ # MS are Hard
140
+ # results.each do |r|
141
+ # stacks.each do |stack|
142
+ # next unless r[stack]
143
+
144
+ # r[stack] = r[stack] * 1000
145
+ # end
146
+ # end
147
+
148
+ # Build List
149
+ stacks.each do |stack|
150
+ data = default_index.clone
151
+ data.transform_values! { |_y| [] } # Ensure Uniq
152
+ list[stack] = {
153
+ name: stack, data: data
154
+ }
155
+ end
156
+
157
+ # Collect Stack Data
158
+ results.each do |r|
159
+ stacks.each do |stack|
160
+ next unless r.key?(stack)
161
+
162
+ list[stack].data[r.time.floor(interval)] << r[stack]
163
+ end
164
+ end
165
+
166
+ # Transform / Calculate
167
+ list.each do |_k, v|
168
+ v.data.transform_values! do |l|
169
+ l.empty? ? 0 : (l.sum / l.size.to_f)
170
+ end
171
+ end
172
+
173
+ list.values
174
+ end
175
+
176
+ # Helper to get the bottom Ten
177
+ def top_method_calls(list)
178
+ sorted = list.sort_by(&:last)
179
+
180
+ all = if sorted.length < 10
181
+ sorted
182
+ else
183
+ sorted[-10..]
184
+ end
185
+
186
+ all.map(&:first)
187
+ end
188
+
189
+ def build_count_by_occurance(results, key, limit = 10)
190
+ output = results.each_with_object(Hash.new(0)) do |result, counts|
191
+ counts[result[key]] += 1
192
+ end
193
+
194
+ return output if limit.zero?
195
+
196
+ output.sort_by(&:last).reverse[0..(limit - 1)].to_h
197
+ end
198
+ end
@@ -0,0 +1,104 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ # ======================================================
4
+ # == [ Production Log ]
5
+ # gitlab-rails/production_json.log
6
+ # ======================================================
7
+ # def prod_avg_path_duration_series
8
+ # query = [
9
+ # 'gitlab-rails/production_json.log',
10
+ # '--slice=time,path,duration_s'
11
+ # ].join(' ')
12
+
13
+ # results = GreenHat::ShellHelper.filter_internal(query)
14
+
15
+ # method_key = :path
16
+ # duration_key = :duration_s
17
+
18
+ # # Collect Total by Duration
19
+ # top = results.each_with_object({}) do |v, obj|
20
+ # obj[v[method_key]] ||= []
21
+ # obj[v[method_key]] << v[duration_key]
22
+
23
+ # obj
24
+ # end
25
+
26
+ # # Average / Round
27
+ # top.transform_values! { |x| (x.sum / x.size.to_f).round(1) }
28
+
29
+ # # Only top by duration
30
+ # method_calls = top_method_calls(top)
31
+ # results.select! { |x| method_calls.any? x[method_key] }
32
+
33
+ # build_group_time_avg(results, method_key, duration_key) # 5 Minutes
34
+ # end
35
+
36
+ def prod_status_series
37
+ query = [
38
+ 'gitlab-rails/production_json.log',
39
+ '--slice=time,status',
40
+ '--exists=status'
41
+ ].join(' ')
42
+
43
+ results = GreenHat::ShellHelper.filter_internal(query)
44
+
45
+ build_group_time_total(results, :status)
46
+ end
47
+
48
+ def prod_duration_series
49
+ query = [
50
+ 'gitlab-rails/production_json.log',
51
+ '--slice=time,path,duration_s',
52
+ '--exists=duration_s'
53
+ ].join(' ')
54
+
55
+ # MS is hard
56
+ results = GreenHat::ShellHelper.filter_internal(query)
57
+ results.each do |r|
58
+ r[:duration_s] = r[:duration_s] * 1000
59
+ end
60
+
61
+ # build_time_avg(results, :duration_s).transform_values { |x| x.round(1) }
62
+ build_percentile_list(results, :duration_s)
63
+ end
64
+
65
+ def prod_duration_series_stacked
66
+ stacks = %i[
67
+ redis_duration_s
68
+ view_duration_s
69
+ gitaly_duration_s
70
+ redis_cache_duration_s
71
+ duration_s
72
+ db_duration_s
73
+ redis_shared_state_duration_s
74
+ queue_duration_s
75
+ db_duration_s
76
+ ]
77
+
78
+ query = [
79
+ 'gitlab-rails/production_json.log',
80
+ "--slice=time,#{stacks.join(',')}"
81
+ ].join(' ')
82
+
83
+ results = GreenHat::ShellHelper.filter_internal(query)
84
+
85
+ output = build_stack_time_avg(results, stacks)
86
+
87
+ # MS are hard
88
+ output.each do |dataset|
89
+ dataset[:data].transform_values! { |x| (x * 1000).round(1) }
90
+ end
91
+
92
+ output
93
+ end
94
+
95
+ def prod_duration_pie
96
+ all = faststats_run('gitlab-rails/production_json.log', 'top')[:totals]
97
+
98
+ # Grab Specifics
99
+ totals = all.except(:count, :fails).transform_values(&:round)
100
+
101
+ # Organize
102
+ totals.sort_by(&:last).reverse.to_h
103
+ end
104
+ end
@@ -0,0 +1,73 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ # ======================================================
4
+ # == [ Sidekiq ]
5
+ # ======================================================
6
+ def sidekiq_avg_duration_series
7
+ query = [
8
+ 'sidekiq/current',
9
+ '--slice=time,class,duration_s',
10
+ '--duration_s>=1'
11
+ ].join(' ')
12
+
13
+ results = GreenHat::ShellHelper.filter_internal(query)
14
+
15
+ method_key = :class
16
+ duration_key = :duration_s
17
+
18
+ # Collect Total by Duration
19
+ top = results.each_with_object({}) do |v, obj|
20
+ obj[v[method_key]] ||= []
21
+ obj[v[method_key]] << v[duration_key]
22
+
23
+ obj
24
+ end
25
+
26
+ # Average / Round
27
+ top.transform_values! { |x| (x.sum / x.size.to_f).round(1) }
28
+
29
+ # Only top by duration
30
+ method_calls = top_method_calls(top)
31
+ results.select! { |x| method_calls.any? x[method_key] }
32
+
33
+ build_group_time_avg(results, method_key, duration_key) # 5 Minutes
34
+ end
35
+
36
+ def sidekiq_duration
37
+ query = [
38
+ 'sidekiq/current',
39
+ '--slice=time,duration_s',
40
+ '--exists=duration_s',
41
+ '--duration_s!=0',
42
+ '--exact'
43
+ ].join(' ')
44
+
45
+ results = GreenHat::ShellHelper.filter_internal(query)
46
+
47
+ build_percentile_list(results, :duration_s)
48
+ end
49
+
50
+ def sidekiq_errors_series
51
+ query = [
52
+ 'sidekiq/current',
53
+ '--slice=time,class',
54
+ '--exists=errors'
55
+ ].join(' ')
56
+
57
+ results = GreenHat::ShellHelper.filter_internal(query)
58
+
59
+ build_group_time_total(results, :class)
60
+ end
61
+
62
+ def sidekiq_job_latency
63
+ query = [
64
+ 'sidekiq/current',
65
+ '--slice=time,class,scheduling_latency_s',
66
+ '--exists=scheduling_latency_s'
67
+ ].join(' ')
68
+
69
+ results = GreenHat::ShellHelper.filter_internal(query)
70
+
71
+ build_percentile_list(results, :scheduling_latency_s)
72
+ end
73
+ end
@@ -0,0 +1,74 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ # Time / Percentile without Grouping
4
+ def build_time_percentile(results, sum, percentile = 0.90, interval = 5.minutes)
5
+ index = build_time_index(results, interval)
6
+
7
+ index.transform_values! { |_y| [] }
8
+
9
+ results.each do |r|
10
+ index[r.time.floor(interval)] << r[sum]
11
+ end
12
+
13
+ index.transform_values! do |l|
14
+ l.percentile(percentile)
15
+ end
16
+
17
+ index
18
+ end
19
+
20
+ # Time / Min/Max without Grouping
21
+ def build_time_method(results, sum, method = :max, interval = 5.minutes)
22
+ index = build_time_index(results, interval)
23
+
24
+ index.transform_values! { |_y| [] }
25
+
26
+ results.each do |r|
27
+ index[r.time.floor(interval)] << r[sum]
28
+ end
29
+
30
+ index.transform_values! do |l|
31
+ next if l.empty?
32
+
33
+ l.send(method)
34
+ end
35
+
36
+ index
37
+ end
38
+
39
+ # Helper to make rounding Values Easier
40
+ def round_values(results, round_value = 1)
41
+ results.transform_values { |x| x.round(round_value) }
42
+ end
43
+
44
+ # Helper to grab the most common desired results
45
+ def build_percentile_list(results, key, round_value = 1)
46
+ p99 = build_time_percentile(results, key, 0.99)
47
+ p95 = build_time_percentile(results, key, 0.95)
48
+ max = build_time_method(results, key, :max)
49
+ mean = build_time_method(results, key, :mean)
50
+ [
51
+ { name: 'p99', data: round_values(p99, round_value) },
52
+ { name: 'p95', data: round_values(p95, round_value) },
53
+ { name: 'mean', data: round_values(mean, round_value) },
54
+ { name: 'max', data: round_values(max, round_value) }
55
+ ]
56
+ end
57
+ end
58
+
59
+ # May be worth adding other stats at some point
60
+ # p99 = build_time_percentile(results, :duration_ms, 0.99)
61
+ # p95 = build_time_percentile(results, :duration_ms, 0.95)
62
+ # min = build_time_method(results, :duration_ms, :min)
63
+ # max = build_time_method(results, :duration_ms, :max)
64
+ # median = build_time_method(results, :duration_ms, :median)
65
+ # mean = build_time_method(results, :duration_ms, :mean)
66
+
67
+ # [
68
+ # { name: 'p99', data: round_values(p99) },
69
+ # { name: 'p95', data: round_values(p95) },
70
+ # { name: 'median', data: median },
71
+ # { name: 'mean', data: round_values(mean) },
72
+ # { name: 'min', data: min },
73
+ # { name: 'max', data: max }
74
+ # ]
@@ -0,0 +1,36 @@
1
+ # Time Series Helpers
2
+ module TimeHelpers
3
+ def time_series
4
+ results = GreenHat::ShellHelper.filter_internal(params[:query])
5
+
6
+ # Prevent exceptions on empty results
7
+ return [] if results.empty?
8
+
9
+ # Ensure Time
10
+ results.select! { |x| x.key?(:time) && !x[:time].nil? }
11
+
12
+ if !params[:avg].blank?
13
+ build_group_time_avg(results, group_sym, avg_sym, param_interval)
14
+ elsif !params[:group].blank?
15
+ build_group_time_total(results, group_sym, param_interval)
16
+ else
17
+ build_time_total(results, param_interval)
18
+ end
19
+ end
20
+
21
+ def param_interval
22
+ if params[:interval]
23
+ params[:interval].to_i.minutes
24
+ else
25
+ 5.minutes
26
+ end
27
+ end
28
+
29
+ def group_sym
30
+ params[:group].strip.to_sym
31
+ end
32
+
33
+ def avg_sym
34
+ params[:avg].strip.to_sym
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ # General Web Helper
2
+ module WebHelpers
3
+ # ======================================================
4
+ # == [ API ]
5
+ # ======================================================
6
+ def workhorse_request_total
7
+ query = [
8
+ 'gitlab-workhorse/current',
9
+ '--slice=time,status',
10
+ '--exists=status'
11
+ ].join(' ')
12
+
13
+ results = GreenHat::ShellHelper.filter_internal(query)
14
+
15
+ build_group_time_total(results, :status)
16
+ end
17
+
18
+ def workhorse_client_total
19
+ query = [
20
+ 'gitlab-workhorse/current',
21
+ '--slice=user_agent',
22
+ '--exists=user_agent'
23
+ ].join(' ')
24
+
25
+ results = GreenHat::ShellHelper.filter_internal(query)
26
+
27
+ build_count_by_occurance(results, :user_agent, 5)
28
+ end
29
+
30
+ def workhorse_duration
31
+ query = [
32
+ 'gitlab-workhorse/current',
33
+ '--slice=time,duration_ms',
34
+ '--exists=duration_ms',
35
+ '--duration_ms!=0',
36
+ '--exact'
37
+ ].join(' ')
38
+
39
+ results = GreenHat::ShellHelper.filter_internal(query)
40
+
41
+ build_percentile_list(results, :duration_ms)
42
+ end
43
+ end
data/lib/greenhat/web.rb CHANGED
@@ -1,46 +1,90 @@
1
1
  require 'sinatra/base'
2
2
 
3
+ # Chart Shims
4
+ # https://github.com/ankane/chartkick/blob/master/lib/chartkick.rb
5
+ require 'chartkick/enumerable'
6
+ require 'chartkick/helper'
7
+ require 'chartkick/version'
8
+ require 'chartkick/sinatra'
9
+
10
+ # Load Web Helpers
11
+ require_all "#{File.dirname(__FILE__)}/web"
12
+
3
13
  # Helper
4
14
  module GreenHat
5
15
  # Web Helper
6
16
  module Web
7
17
  # Template Helper
8
18
  class MyApp < Sinatra::Base
9
- set :server, :puma
10
- set :server_settings, log_requests: false, debug: false, stdout_redirect: '/dev/null', quiet: true,
11
- Port: 15_123
12
- set :logging, false
19
+ helpers Chartkick::Helper
20
+ helpers ::WebHelpers
21
+ helpers ::TimeHelpers
22
+
23
+ configure do
24
+ set :server, :puma
25
+ set :port, 4567
26
+ set :quiet, true
27
+ set :server_settings, Silent: true
28
+ end
13
29
 
14
30
  get '/' do
15
- 'Hello world!'
31
+ slim :index
16
32
  end
17
- end
18
33
 
19
- def self.start
20
- @thread = Thread.new do
21
- # original_stderr = $stderr.clone
22
- # original_stdout = $stdout.clone
23
- # $stderr.reopen(File.new('/dev/null', 'w'))
24
- # $stdout.reopen(File.new('/dev/null', 'w'))
34
+ get '/chart' do
35
+ slim :chart
36
+ end
25
37
 
26
- app = MyApp.run!
27
- # $stdout.reopen(original_stdout)
28
- # $stderr.reopen(original_stderr)
38
+ get '/charts/:chart_type' do
39
+ # TODO: Better validation?
40
+ slim params[:chart_type].to_sym
41
+ end
29
42
 
30
- # $stdout.reopen($STDOUT_CLONE)
43
+ get '/chart/time' do
44
+ @index = time_series unless params[:query].empty?
45
+ slim :time
46
+ end
31
47
 
32
- app
48
+ get '/chart/chartkick.js' do
49
+ content_type 'application/javascript'
50
+ File.read("#{File.dirname(__FILE__)}/views/chartkick.js")
33
51
  end
52
+ end
34
53
 
35
- sleep 0.2
54
+ def self.setup
55
+ Archive.all.map(&:host_create).map(&:save!)
56
+ end
57
+
58
+ def self.toggle
59
+ if alive?
60
+ LogBot.info('Stopping Web')
61
+ stop
62
+ else
63
+ start
64
+ end
65
+ end
66
+
67
+ def self.start
68
+ setup if Host.count.zero?
69
+
70
+ return true if alive? # Prevent Dupes
71
+
72
+ LogBot.info('Starting Web')
73
+
74
+ @thread = Thread.new { MyApp.run! }
75
+ # MyApp.run! # Debugging
36
76
  end
37
77
 
38
78
  def self.thread
39
79
  @thread
40
80
  end
41
81
 
82
+ def self.alive?
83
+ !thread.nil? && thread.alive?
84
+ end
85
+
42
86
  def self.stop
43
- thread.kill if thread.alive?
87
+ thread.kill if alive?
44
88
  end
45
89
  end
46
90
  end