watchmonkey_cli 1.9.0 → 1.12.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.
@@ -0,0 +1,566 @@
1
+ module WatchmonkeyCli
2
+ class TelegramBot
3
+ BotProbeError = Class.new(::RuntimeError)
4
+ UnknownEgressType = Class.new(::RuntimeError)
5
+
6
+ class Event
7
+ attr_reader :app, :bot
8
+
9
+ def initialize app, bot, event
10
+ @app, @bot, @event = app, bot, event
11
+ end
12
+
13
+ def raw
14
+ @event
15
+ end
16
+
17
+ def method_missing method, *args, &block
18
+ @event.__send__(method, *args, &block)
19
+ end
20
+
21
+ def user_data
22
+ app.telegram_bot_get_udata(raw.from.id)
23
+ end
24
+
25
+ def user_admin?
26
+ user_data && user_data[1].include?(:admin_flag)
27
+ end
28
+
29
+ def from_descriptive
30
+ "".tap do |name|
31
+ name << "[BOT] " if from.is_bot
32
+ name << "#{from.first_name} " if from.first_name.present?
33
+ name << "#{from.last_name} " if from.last_name.present?
34
+ name << "(#{from.username}) " if from.username.present?
35
+ name << "[##{from.id}]"
36
+ end.strip
37
+ end
38
+
39
+ def message?
40
+ raw.is_a?(Telegram::Bot::Types::Message)
41
+ end
42
+
43
+ def message
44
+ return false unless message?
45
+ raw.text.to_s
46
+ end
47
+
48
+ def chunks
49
+ @_chunks ||= message.split(" ")
50
+ end
51
+
52
+ def command?
53
+ raw.is_a?(Telegram::Bot::Types::Message) && message.start_with?("/")
54
+ end
55
+
56
+ def command
57
+ chunks.first if command?
58
+ end
59
+
60
+ def args
61
+ chunks[1..-1]
62
+ end
63
+
64
+ def reply msg, msgopts = {}
65
+ return unless msg.present?
66
+ msgopts = msgopts.merge(text: msg, chat_id: raw.chat.id)
67
+ msgopts = msgopts.merge(reply_to_message_id: raw.message_id) unless msgopts[:quote] == false
68
+ app.telegram_bot_send(msgopts.except(:quote))
69
+ end
70
+ end
71
+
72
+ class BetterQueue
73
+ def initialize
74
+ @stor = []
75
+ @monitor = Monitor.new
76
+ end
77
+
78
+ def sync &block
79
+ @monitor.synchronize(&block)
80
+ end
81
+
82
+ [:length, :empty?, :push, :pop, :unshift, :shift, :<<].each do |meth|
83
+ define_method(meth) do |*args, &block|
84
+ sync { @stor.send(meth, *args, &block) }
85
+ end
86
+ end
87
+ end
88
+
89
+ class PromiseHandler < BetterQueue
90
+ def scrub!
91
+ @stor.dup.each_with_index do |p, i|
92
+ @stor.delete(p) if [:fulfilled, :rejected].include?(p.state)
93
+ end
94
+ end
95
+
96
+ def await_all!
97
+ while p = shift
98
+ p.wait
99
+ end
100
+ end
101
+ end
102
+
103
+ def self.hook!(app, bot_opts = {})
104
+ app.opts[:telegram_bot] = bot_opts.reverse_merge({
105
+ debug: false,
106
+ info: true,
107
+ error: true,
108
+ throttle_retention: 30.days,
109
+ retry_on_egress_failure: false,
110
+ notify: []
111
+ })
112
+
113
+ app.instance_eval do
114
+ hook :dispatch_around do |action, &act|
115
+ throw :skip, true unless action == :index
116
+ begin
117
+ require "telegram/bot"
118
+ rescue LoadError
119
+ abort "[TelegramBot] cannot load telegram/bot, run\n\t# gem install telegram-bot-ruby"
120
+ end
121
+ telegram_bot_dispatch(&act)
122
+ end
123
+
124
+ [:debug, :info, :error].each do |level|
125
+ hook :"on_#{level}" do |msg, robj|
126
+ telegram_bot_notify(msg, robj)
127
+ end if app.opts[:telegram_bot][level]
128
+ end
129
+
130
+ hook :wm_shutdown do
131
+ debug "[TelegramBot] shutting down telegram bot ingress thread"
132
+ if @telegram_bot_ingress&.alive?
133
+ @telegram_bot_ingress[:stop] = true
134
+ @telegram_bot_ingress.join
135
+ end
136
+
137
+ debug "[TelegramBot] shutting down telegram bot egress thread"
138
+ if @telegram_bot_egress&.alive?
139
+ telegram_bot_msg_admins "<b>ALERT: Watchmonkey is shutting down!</b>", parse_mode: "HTML" rescue false
140
+ @telegram_bot_egress_promises.await_all!
141
+ @telegram_bot_egress[:stop] = true
142
+ @telegram_bot_egress.join
143
+ end
144
+ end
145
+
146
+ # --------------------
147
+
148
+ def telegram_bot_state
149
+ "#{wm_cfg_path}/telegram_bot.wmstate"
150
+ end
151
+
152
+ def telegram_bot_dispatch &act
153
+ parent_thread = Thread.current
154
+ @telegram_bot_throttle_locks = {}
155
+ @telegram_bot_egress_promises = PromiseHandler.new
156
+ @telegram_bot_egress_queue = BetterQueue.new
157
+ telegram_bot_throttle # eager load
158
+ telegram_bot_normalize_notify_udata
159
+
160
+ # ==================
161
+ # = ingress thread =
162
+ # ==================
163
+ debug "[TelegramBot] Starting telegram bot ingress thread..."
164
+ @telegram_bot_ingress = Thread.new do
165
+ Thread.current.abort_on_exception = true
166
+ begin
167
+ until Thread.current[:stop]
168
+ begin
169
+ telegram_bot.fetch_updates {|ev| telegram_bot_handle_event(ev) }
170
+ rescue SocketError, Faraday::ConnectionFailed => ex
171
+ error "[TelegramBot] Poll failure, retrying in 3 seconds (#{ex.class}: #{ex.message})"
172
+ sleep 3 unless Thread.current[:stop]
173
+ retry unless Thread.current[:stop]
174
+ end
175
+ end
176
+ rescue StandardError => ex
177
+ parent_thread.raise(ex)
178
+ end
179
+ end
180
+
181
+ # wait for telegram bot to be ready
182
+ begin
183
+ Timeout::timeout(10) do
184
+ loop do
185
+ break if @telegram_bot_info
186
+ sleep 0.5
187
+ end
188
+ end
189
+ rescue Timeout::Error
190
+ abort "[TelegramBot] Failed to start telegram bot within 10 seconds, aborting...", 2
191
+ end
192
+
193
+ # ==================
194
+ # = egress thread =
195
+ # ==================
196
+ debug "[TelegramBot] Starting telegram bot egress thread..."
197
+ @telegram_bot_egress = Thread.new do
198
+ Thread.current.abort_on_exception = true
199
+ begin
200
+ until Thread.current[:stop] && @telegram_bot_egress_queue.empty?
201
+ begin
202
+ promise, item = @telegram_bot_egress_queue.shift
203
+ if item
204
+ mode = item.delete(:__mode)
205
+ case mode
206
+ when :send then promise.set telegram_bot.api.send_message(item)
207
+ when :edit then promise.set telegram_bot.api.edit_message_text(item)
208
+ else raise(UnknownEgressType, "Unknown egress mode `#{mode}'!")
209
+ end
210
+ else
211
+ @telegram_bot_egress_promises.scrub!
212
+ sleep 0.5
213
+ end
214
+ rescue SocketError, Faraday::ConnectionFailed => ex
215
+ if opts[:telegram_bot][:retry_on_egress_failure]
216
+ error "[TelegramBot] Push failure, retrying in 3 seconds (#{ex.class}: #{ex.message})"
217
+ @telegram_bot_egress_queue.unshift([promise, item.merge(__mode: mode)])
218
+ sleep 3 unless Thread.current[:stop]
219
+ else
220
+ error "[TelegramBot] Push failure, discarding message (#{ex.class}: #{ex.message})"
221
+ sleep 0.5
222
+ end
223
+ end
224
+ end
225
+ rescue StandardError => ex
226
+ parent_thread.raise(ex)
227
+ end
228
+ end
229
+
230
+ telegram_bot_msg_admins "<b>INFO: Watchmonkey is watching#{" ONCE" unless opts[:loop_forever]}!</b>", disable_notification: true, parse_mode: "HTML"
231
+ act.call
232
+ rescue BotProbeError => ex
233
+ abort ex.message
234
+ ensure
235
+ @telegram_bot_egress_promises.await_all!
236
+ File.open(telegram_bot_state, "w+") do |f|
237
+ f << telegram_bot_throttle.to_json
238
+ end
239
+ end
240
+
241
+ def telegram_bot
242
+ @telegram_bot ||= begin
243
+ Telegram::Bot::Client.new(opts[:telegram_bot][:api_key], opts[:telegram_bot].except(:api_key)).tap do |bot|
244
+ begin
245
+ me = bot.api.get_me
246
+ if me["ok"]
247
+ @telegram_bot_info = me["result"]
248
+ info "[TelegramBot] Established client [#{@telegram_bot_info["id"]}] #{@telegram_bot_info["first_name"]} (#{@telegram_bot_info["username"]})"
249
+ else
250
+ raise BotProbeError, "Failed to get telegram client information, got NOK response (#{me.inspect})"
251
+ end
252
+ rescue HTTParty::RedirectionTooDeep, Telegram::Bot::Exceptions::ResponseError => ex
253
+ raise BotProbeError, "Failed to get telegram client information, API key correct? (#{ex.class})"
254
+ rescue SocketError, Faraday::ConnectionFailed => ex
255
+ raise BotProbeError, "Failed to get telegram client information, connection failure? (#{ex.class}: #{ex.message})"
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ def telegram_bot_send payload
262
+ Concurrent::Promise.new.tap do |promise|
263
+ @telegram_bot_egress_queue.push([promise, payload.merge(__mode: :send)])
264
+ end
265
+ end
266
+
267
+ def telegram_bot_edit payload
268
+ Concurrent::Promise.new.tap do |promise|
269
+ @telegram_bot_egress_queue.push([promise, payload.merge(__mode: :edit)])
270
+ end
271
+ end
272
+
273
+ def telegram_bot_tid_exclusive tid, &block
274
+ sync do
275
+ @telegram_bot_throttle_locks[tid] ||= Monitor.new
276
+ end
277
+ @telegram_bot_throttle_locks[tid].synchronize(&block)
278
+ end
279
+
280
+ def telegram_bot_throttle
281
+ @telegram_bot_throttle ||= begin
282
+ Hash.new {|h,k| h[k] = Hash.new }.tap do |h|
283
+ if File.exist?(telegram_bot_state)
284
+ json = File.read(telegram_bot_state)
285
+ if json.present?
286
+ JSON.parse(json).each do |k, v|
287
+ case k
288
+ when "__mute_until"
289
+ h[k] = Time.parse(v)
290
+ else
291
+ v.each do |k, data|
292
+ data[0] = Time.parse(data[0])
293
+ data[1] = Time.parse(data[1])
294
+ end
295
+ v.delete_if {|k, data| data[0] < opts[:telegram_bot][:throttle_retention].ago }
296
+ h[k.to_i] = v
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ def telegram_bot_normalize_notify_udata
306
+ opts[:telegram_bot][:notify].each do |a|
307
+ a[1] = (a[1] ? [*a[1]] : [:error]).flat_map(&:to_sym).uniq
308
+ a[2] ||= {}
309
+ end
310
+ end
311
+
312
+ def telegram_bot_get_udata lookup_tid
313
+ opts[:telegram_bot][:notify].detect do |tid, level, topts|
314
+ tid == lookup_tid
315
+ end
316
+ end
317
+
318
+ def telegram_bot_user_muted? tid, notify = true
319
+ telegram_bot_tid_exclusive(tid) do
320
+ return false unless mute_until = telegram_bot_throttle[tid]["__mute_until"]
321
+ if mute_until > Time.current
322
+ return true
323
+ else
324
+ telegram_bot_throttle[tid].delete("__mute_until")
325
+ telegram_bot_send(text: "You have been unmuted (expired #{mute_until})", chat_id: tid, disable_notification: true) if notify
326
+ return false
327
+ end
328
+ end
329
+ end
330
+
331
+ def telegram_bot_notify msg, robj
332
+ to_notify = opts[:telegram_bot][:notify].select do |tid, level, topts|
333
+ level.include?(:all) || level.include?(robj.type)
334
+ end
335
+
336
+ to_notify.each do |tid, level, topts|
337
+ telegram_bot_tid_exclusive(tid) do
338
+ next if telegram_bot_user_muted?(tid)
339
+ if robj
340
+ # gate only tags
341
+ next if topts[:only] && topts[:only].any? && !robj.tags.any?{|t| topts[:only].include?(t) }
342
+
343
+ # gate except tags
344
+ next if topts[:except] && topts[:except].any? && robj.tags.any?{|t| topts[:except].include?(t) }
345
+
346
+ # send message
347
+ throttle = telegram_bot_throttle[tid][robj.uniqid] ||= [Time.current, Time.current, 0, nil]
348
+ throttle[2] += 1
349
+ if (Time.current - throttle[0]) <= (topts[:throttle] || 0) && throttle[3]
350
+ throttle[1] = Time.current
351
+ _telegram_bot_sendmsg(tid, msg, throttle[2], throttle, throttle[3])
352
+ else
353
+ throttle[0] = Time.current
354
+ throttle[2] = 1
355
+ throttle[3] = nil
356
+ _telegram_bot_sendmsg(tid, msg, throttle[2], throttle)
357
+ end
358
+ else
359
+ _telegram_bot_sendmsg(tid, msg, 0, [])
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ def _telegram_bot_sendmsg tid, msg, repeat, result_to, msgid = nil
366
+ msg = "<pre>#{ERB::Util.h msg}</pre>"
367
+ msg = "#{msg}\n<b>(message repeated #{repeat} times)</b> #{" <i>last occurance: #{result_to[1]}</i>" if result_to[1]}" if repeat > 1 && msgid
368
+ (msgid ?
369
+ telegram_bot_edit(chat_id: tid, message_id: msgid, text: msg, disable_web_page_preview: true, parse_mode: "html")
370
+ :
371
+ telegram_bot_send(chat_id: tid, text: msg, disable_web_page_preview: true, parse_mode: "html")
372
+ ).tap do |promise|
373
+ await = Concurrent::Promise.new
374
+ promise.on_success do |m|
375
+ if msgid = m.dig("result", "message_id")
376
+ result_to[3] = msgid
377
+ await.set :success
378
+ else
379
+ puts m.inspect
380
+ await.set :failed
381
+ end
382
+ end
383
+ promise.on_error do |ex|
384
+ error "[TelegramBot] MessagePromiseError - #{ex.class}:#{ex.message}"
385
+ await.set :failed
386
+ end
387
+ @telegram_bot_egress_promises << await
388
+ end
389
+ rescue
390
+ end
391
+
392
+ def _telegram_bot_timestr_parse *chunks
393
+ time = Time.current
394
+ chunks.flatten.each do |chunk|
395
+ if chunk.end_with?("d")
396
+ time += chunk[0..-2].to_i.days
397
+ elsif chunk.end_with?("h")
398
+ time += chunk[0..-2].to_i.hours
399
+ elsif chunk.end_with?("m")
400
+ time += chunk[0..-2].to_i.minutes
401
+ elsif chunk.end_with?("s")
402
+ time += chunk[0..-2].to_i.seconds
403
+ else
404
+ time += chunk.to_i.seconds
405
+ end
406
+ end
407
+ time
408
+ end
409
+
410
+ def telegram_bot_handle_event ev
411
+ Event.new(self, telegram_bot, ev).tap do |event|
412
+ begin
413
+ ctrl = catch :event_control do
414
+ case event.command
415
+ when "/ping"
416
+ event.reply "Pong!"
417
+ throw :event_control, :done
418
+ when "/start"
419
+ if event.user_data
420
+ event.reply [].tap{|m|
421
+ m << "<b>Welcome!</b> I will tell you if something is wrong with your infrastructure."
422
+ m << "Your current tags are: #{event.user_data[1].join(", ")}"
423
+ m << "<b>You have admin permissions!</b>" if event.user_admin?
424
+ m << "\nInstead of muting me in Telegram you can silence me for a while with <code>/mute 6h 30m</code>."
425
+ m << "Use <code>/help</code> for more information."
426
+ }.join("\n"), parse_mode: "HTML"
427
+ else
428
+ event.reply %{
429
+ Hello there, unfortunately I don't recognize your user id (#{event.from.id}).
430
+ Please ask your admin to add you to the configuration file.
431
+ }
432
+ end
433
+ throw :event_control, :done
434
+ when "/mute"
435
+ if event.user_data
436
+ telegram_bot_tid_exclusive(event.from.id) do
437
+ telegram_bot_user_muted?(event.from.id) # clears if expired
438
+ if event.args.any?
439
+ mute_until = _telegram_bot_timestr_parse(event.args)
440
+ telegram_bot_throttle[event.from.id]["__mute_until"] = mute_until
441
+ event.reply "You are muted until #{mute_until}\nUse /unmute to prematurely cancel the mute.", disable_notification: true
442
+ else
443
+ msg = "Usage: <code>/mute &lt;1d 2h 3m 4s&gt;</code>"
444
+ if mute_until = telegram_bot_throttle[event.from.id]["__mute_until"]
445
+ msg << "\n<b>You are currently muted until #{mute_until}</b> /unmute"
446
+ end
447
+ event.reply msg, parse_mode: "HTML", disable_notification: true
448
+ end
449
+ end
450
+ throw :event_control, :done
451
+ else
452
+ throw :event_control, :access_denied
453
+ end
454
+ when "/unmute"
455
+ if event.user_data
456
+ telegram_bot_tid_exclusive(event.from.id) do
457
+ if mute_until = telegram_bot_throttle[event.from.id]["__mute_until"]
458
+ event.reply "You have been unmuted (prematurely canceled #{mute_until})", disable_notification: true
459
+ else
460
+ event.reply "You are not currently muted.", disable_notification: true
461
+ end
462
+ end
463
+ throw :event_control, :done
464
+ else
465
+ throw :event_control, :access_denied
466
+ end
467
+ when "/scan", "/rescan"
468
+ if event.user_data
469
+ if respond_to?(:requeue_runall)
470
+ event.reply "Triggering all tasks in ReQueue…", disable_notification: true
471
+ requeue_runall
472
+ else
473
+ event.reply "Watchmonkey is not running with ReQueue!", disable_notification: true
474
+ end
475
+ throw :event_control, :done
476
+ else
477
+ throw :event_control, :access_denied
478
+ end
479
+ when "/clearthrottle", "/clearthrottles"
480
+ if event.user_data
481
+ telegram_bot_tid_exclusive(event.from.id) do
482
+ was_muted = telegram_bot_throttle[event.from.id].delete("__mute_until")
483
+ telegram_bot_throttle.delete(event.from.id)
484
+ event.reply "Cleared all your throttles!", disable_notification: true
485
+ if was_muted
486
+ telegram_bot_throttle[event.from.id]["__mute_until"] = was_muted
487
+ end
488
+ end
489
+ throw :event_control, :done
490
+ else
491
+ throw :event_control, :access_denied
492
+ end
493
+ when "/status"
494
+ if event.user_admin?
495
+ msg = []
496
+ msg << "<pre>"
497
+ msg << "========== STATUS =========="
498
+ msg << " Queue: #{@queue.length}"
499
+ msg << " Requeue: #{@requeue.length}" if @requeue
500
+ msg << " Workers: #{@threads.select{|t| t[:working] }.length}/#{@threads.length} working (#{@threads.select(&:alive?).length} alive)"
501
+ msg << " Threads: #{filtered_threads.length}"
502
+ msg << " Processed: #{@processed}"
503
+ msg << " Promises: #{@telegram_bot_egress_promises.length}"
504
+ msg << "========== //STATUS =========="
505
+ msg << "</pre>"
506
+ event.reply msg.join("\n"), parse_mode: "HTML", disable_notification: true
507
+ throw :event_control, :done
508
+ else
509
+ throw :event_control, :access_denied
510
+ end
511
+ when "/help"
512
+ event.reply [].tap{|m|
513
+ m << "<b>/start</b> Bot API"
514
+ if event.user_data
515
+ m << "<b>/mute &lt;time&gt;</b> Don't send messages for this long (e.g. <code>/mute 1d 2h 3m 4s</code>)"
516
+ m << "<b>/unmute</b> Cancel mute"
517
+ m << "<b>/scan</b> Trigger all queued scans in ReQueue"
518
+ m << "<b>/clearthrottle</b> Clears your throttled messages"
519
+ end
520
+ if event.user_admin?
521
+ m << "<b>/status</b> Some status information"
522
+ m << "<b>/wm_shutdown</b> Shutdown watchmonkey process, may respawn if deamonized"
523
+ end
524
+ }.join("\n"), parse_mode: "HTML", disable_notification: true
525
+ throw :event_control, :done
526
+ when "/wm_shutdown"
527
+ if event.user_admin?
528
+ $wm_runtime_exiting = true
529
+ telegram_bot_msg_admins "ALERT: `#{event.from_descriptive}` invoked shutdown!"
530
+ throw :event_control, :done
531
+ else
532
+ throw :event_control, :access_denied
533
+ end
534
+ end
535
+ end
536
+
537
+ if ctrl == :access_denied
538
+ event.reply("You don't have sufficient permissions to execute this command!")
539
+ elsif ctrl == :bad_request
540
+ event.reply("Bad Request: Your input is invalid!")
541
+ end
542
+ rescue StandardError => ex
543
+ event.reply("Sorry, encountered an error while processing your request!")
544
+
545
+ # notify admins
546
+ msg = "ERROR: Encountered error while processing a request!\n"
547
+ msg << "Request: #{ERB::Util.h event.message}\n"
548
+ msg << "Origin: #{ERB::Util.h event.from.inspect}\n"
549
+ msg << "<pre>#{ERB::Util.h ex.class}: #{ERB::Util.h ex.message}\n#{ERB::Util.h ex.backtrace.join("\n")}</pre>"
550
+ telegram_bot_msg_admins(msg, parse_mode: "HTML")
551
+ end
552
+ end
553
+ end
554
+
555
+ def telegram_bot_msg_admins msg, msgopts = {}
556
+ return unless msg.present?
557
+ opts[:telegram_bot][:notify].each do |tid, level, topts|
558
+ next unless level.include?(:admin_flag)
559
+ next if telegram_bot_user_muted?(tid)
560
+ telegram_bot_send(msgopts.merge(text: msg, chat_id: tid))
561
+ end
562
+ end
563
+ end
564
+ end
565
+ end
566
+ end
@@ -1,7 +1,10 @@
1
1
  module WatchmonkeyCli
2
2
  class LoopbackConnection
3
+ attr_reader :opts, :established
4
+
3
5
  def initialize(id, opts = {}, &initializer)
4
6
  @id = id
7
+ @established = false
5
8
  @opts = {}.merge(opts)
6
9
  # @mutex = Monitor.new
7
10
  initializer.try(:call, @opts)
@@ -15,6 +18,10 @@ module WatchmonkeyCli
15
18
  "lo:#{@id}"
16
19
  end
17
20
 
21
+ def established?
22
+ @established
23
+ end
24
+
18
25
  def sync &block
19
26
  # @mutex.synchronize(&block)
20
27
  block.try(:call)
@@ -4,6 +4,7 @@ module WatchmonkeyCli
4
4
 
5
5
  def initialize(id, opts = {}, &initializer)
6
6
  @id = id
7
+ @established = false
7
8
 
8
9
  if opts.is_a?(String)
9
10
  u, h = opts.split("@", 2)
@@ -30,6 +31,10 @@ module WatchmonkeyCli
30
31
  "ssh:#{@id}"
31
32
  end
32
33
 
34
+ def established?
35
+ @established
36
+ end
37
+
33
38
  def sync &block
34
39
  @mutex.synchronize(&block)
35
40
  end
@@ -42,7 +47,12 @@ module WatchmonkeyCli
42
47
  end
43
48
 
44
49
  def connection
45
- sync { @ssh ||= Net::SSH.start(nil, nil, @opts) }
50
+ sync {
51
+ @ssh ||= begin
52
+ @established = true
53
+ Net::SSH.start(nil, nil, @opts)
54
+ end
55
+ }
46
56
  end
47
57
 
48
58
  def close!
@@ -1,4 +1,4 @@
1
1
  module WatchmonkeyCli
2
- VERSION = "1.9.0"
2
+ VERSION = "1.12.0"
3
3
  UPDATE_URL = "https://raw.githubusercontent.com/2called-chaos/watchmonkey_cli/master/VERSION"
4
4
  end
@@ -33,6 +33,7 @@ require "watchmonkey_cli/application/dispatch"
33
33
  require "watchmonkey_cli/application"
34
34
 
35
35
  # require buildin checkers
36
+ require "watchmonkey_cli/checkers/dev_pry"
36
37
  require "watchmonkey_cli/checkers/ftp_availability"
37
38
  require "watchmonkey_cli/checkers/mysql_replication"
38
39
  require "watchmonkey_cli/checkers/ssl_expiration"
@@ -45,4 +46,5 @@ require "watchmonkey_cli/checkers/unix_file_exists"
45
46
  require "watchmonkey_cli/checkers/unix_load"
46
47
  require "watchmonkey_cli/checkers/unix_mdadm"
47
48
  require "watchmonkey_cli/checkers/unix_memory"
49
+ require "watchmonkey_cli/checkers/unix_cpu_governor"
48
50
  require "watchmonkey_cli/checkers/www_availability"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: watchmonkey_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Pachnit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-16 00:00:00.000000000 Z
11
+ date: 2021-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -123,6 +123,7 @@ files:
123
123
  - lib/watchmonkey_cli/application/dispatch.rb
124
124
  - lib/watchmonkey_cli/application/output_helper.rb
125
125
  - lib/watchmonkey_cli/checker.rb
126
+ - lib/watchmonkey_cli/checkers/dev_pry.rb
126
127
  - lib/watchmonkey_cli/checkers/ftp_availability.rb
127
128
  - lib/watchmonkey_cli/checkers/mysql_replication.rb
128
129
  - lib/watchmonkey_cli/checkers/ssl_expiration.rb
@@ -140,6 +141,7 @@ files:
140
141
  - lib/watchmonkey_cli/helper.rb
141
142
  - lib/watchmonkey_cli/hooks/platypus.rb
142
143
  - lib/watchmonkey_cli/hooks/requeue.rb
144
+ - lib/watchmonkey_cli/hooks/telegram_bot.rb
143
145
  - lib/watchmonkey_cli/loopback_connection.rb
144
146
  - lib/watchmonkey_cli/ssh_connection.rb
145
147
  - lib/watchmonkey_cli/version.rb
@@ -163,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
165
  - !ruby/object:Gem::Version
164
166
  version: '0'
165
167
  requirements: []
166
- rubygems_version: 3.1.2
168
+ rubygems_version: 3.1.4
167
169
  signing_key:
168
170
  specification_version: 4
169
171
  summary: Watchmonkey CLI - dead simple agentless monitoring via SSH, HTTP, FTP, etc.