watchmonkey_cli 1.9.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/README.md +14 -2
- data/VERSION +1 -1
- data/doc/checker_example.rb +10 -0
- data/doc/config_example.rb +56 -0
- data/lib/watchmonkey_cli/application/configuration.rb +37 -2
- data/lib/watchmonkey_cli/application/configuration.tpl +22 -1
- data/lib/watchmonkey_cli/application/core.rb +55 -5
- data/lib/watchmonkey_cli/application/dispatch.rb +6 -2
- data/lib/watchmonkey_cli/application/output_helper.rb +4 -0
- data/lib/watchmonkey_cli/application.rb +29 -3
- data/lib/watchmonkey_cli/checker.rb +30 -6
- data/lib/watchmonkey_cli/checkers/dev_pry.rb +29 -0
- data/lib/watchmonkey_cli/checkers/ftp_availability.rb +1 -1
- data/lib/watchmonkey_cli/checkers/ssl_expiration.rb +1 -1
- data/lib/watchmonkey_cli/checkers/unix_defaults.rb +3 -1
- data/lib/watchmonkey_cli/checkers/www_availability.rb +2 -1
- data/lib/watchmonkey_cli/hooks/platypus.rb +2 -2
- data/lib/watchmonkey_cli/hooks/requeue.rb +12 -1
- data/lib/watchmonkey_cli/hooks/telegram_bot.rb +566 -0
- data/lib/watchmonkey_cli/loopback_connection.rb +7 -0
- data/lib/watchmonkey_cli/ssh_connection.rb +11 -1
- data/lib/watchmonkey_cli/version.rb +1 -1
- data/lib/watchmonkey_cli.rb +2 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d2615fa891c7ccb38541934fa918c33fa37a93cc3d3c52ece8db41a100e1d0b
|
4
|
+
data.tar.gz: 98f2a66da3fdc810c9b7fc1396ba12a605f3cd12d939585e9c9ed03d9f252fbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23307a955c34627a32e0023df5dc40874eda5d72e80f152e2a57bcf12be767b34db53fc565ffbc8329060cbfe8e72871956123bf553f0ff409ed4468528bca0f
|
7
|
+
data.tar.gz: c671b5bac01cf8c423eef81ada024cd35a1c82f426c9fa80cee4cb90047aa242c579a39248ef349d2cc69d7be281037caac74afa410d59af633029f5b5860b52
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,10 +4,11 @@ Watchmonkey is a very simple tool to monitor resources with Ruby without the nee
|
|
4
4
|
|
5
5
|
Before looking any further you might want to know:
|
6
6
|
|
7
|
-
* There is no escalation or notification system but you may add it yourself
|
8
|
-
* I created this for being used with [Platypus](http://sveinbjorn.org/platypus) hence the [Platypus Hook](https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/platypus.rb)
|
7
|
+
* There is no escalation or notification system (except experimental telegram bot) but you may add it yourself
|
8
|
+
* I originally created this for being used with [Platypus](http://sveinbjorn.org/platypus) hence the [Platypus Hook](https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/platypus.rb)
|
9
9
|
* This is how the text output looks like: [Screenshot](http://imgur.com/8yLYnKb)
|
10
10
|
* This is how the Platypus support looks like: [ProgressBar](http://imgur.com/Vd8ZD7A) [HTML/WebView](http://imgur.com/5FwmWFZ)
|
11
|
+
* This is how Telegram Bot looks for now: [Telegram Screenshot](http://imgur.com/HBONi51)
|
11
12
|
|
12
13
|
---
|
13
14
|
|
@@ -43,6 +44,8 @@ To get a list of available options invoke Watchmonkey with the `--help` or `-h`
|
|
43
44
|
--generate-config [myconfig] Generates a example config in ~/.watchmonkey
|
44
45
|
-l, --log [file] Log to file, defaults to ~/.watchmonkey/logs/watchmonkey.log
|
45
46
|
-t, --threads [NUM] Amount of threads to be used for checking (default: 10)
|
47
|
+
-e, --except tag1,tag2 Don't run tasks tagged with given tags
|
48
|
+
-o, --only tag1,tag2 Only run tasks tagged with given tags
|
46
49
|
-s, --silent Only print errors and infos
|
47
50
|
-q, --quiet Only print errors
|
48
51
|
|
@@ -81,6 +84,15 @@ If you want to monitor something that is not covered by the buildin handlers you
|
|
81
84
|
By default Watchmonkey will run all tests once and then exit. This addon will enable Watchmonkey to run in a loop and run tests on a periodic interval.
|
82
85
|
Since this seems like a core feature it might get included directly into Watchmonkey but for now take a look at the [application configuration file](https://github.com/2called-chaos/watchmonkey_cli/blob/master/doc/config_example.rb) and [ReQueue source code](https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/requeue.rb) for integration examples.
|
83
86
|
|
87
|
+
### Telegram Bot
|
88
|
+
Notify via Telegram. Experimental. Refer to [application configuration file](https://github.com/2called-chaos/watchmonkey_cli/blob/master/doc/config_example.rb) and [TelegramBot source code](https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/telegram_bot.rb) for further information.
|
89
|
+
|
90
|
+
* works with ReQueue (wouldn't make much sense otherwise huh?)
|
91
|
+
* optional per-user message throttling via checker uniqid (checker name + host + arguments)
|
92
|
+
* optional per-user only/except filters based on tags
|
93
|
+
* planned: robust telegram connection failure handling
|
94
|
+
* planned: per-user regex exclusion filters
|
95
|
+
|
84
96
|
### Platypus support
|
85
97
|
[Platypus](http://sveinbjorn.org/platypus) is a MacOS software to create dead simple GUI wrappers for scripts. There is buildin support for the interface types ProgressBar and WebView. For integration examples take a look at the [application configuration file](https://github.com/2called-chaos/watchmonkey_cli/blob/master/doc/config_example.rb) and [Platypus hook source code](https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/platypus.rb).
|
86
98
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.12.0
|
data/doc/checker_example.rb
CHANGED
@@ -12,9 +12,18 @@ module MyWatchmonkeyCheckers
|
|
12
12
|
# e.g. my_checker "http://google.com", some_option: true
|
13
13
|
self.checker_name = "my_checker"
|
14
14
|
|
15
|
+
# Maximum amount of time this task may run before it gets killed.
|
16
|
+
# Set to 0/false to have no time limit whatsoever.
|
17
|
+
# Set to proc to evaluate at runtime
|
18
|
+
# Defaults to app.opts[:maxrt] if nil/unset
|
19
|
+
#self.maxrt = false
|
20
|
+
#self.maxrt = 5.minutes
|
21
|
+
#self.maxrt = ->(app, checker, args){ app.opts[:maxrt] && app.opts[:maxrt] * 2 }
|
22
|
+
|
15
23
|
# Called by configuration defining a check with all the arguments.
|
16
24
|
# e.g. my_checker "http://google.com", some_option: true
|
17
25
|
# Should invoke `app.enqueue` which will by default call `#check!` method with given arguments.
|
26
|
+
# Must have options as last argument!
|
18
27
|
def enqueue host, opts = {}
|
19
28
|
opts = { some_option: false }.merge(opts)
|
20
29
|
|
@@ -28,6 +37,7 @@ module MyWatchmonkeyCheckers
|
|
28
37
|
end
|
29
38
|
|
30
39
|
# First argument is the result object, all other arguments came from `app.enqueue` call.
|
40
|
+
# Must have options as last argument!
|
31
41
|
def check! result, host, opts = {}
|
32
42
|
# Do your checks and modify the result object.
|
33
43
|
# Debug messages will not show if -s/--silent or -q/--quiet argument is passed.
|
data/doc/config_example.rb
CHANGED
@@ -43,3 +43,59 @@ if @argv.delete("--platypus")
|
|
43
43
|
|
44
44
|
@opts[:colorize] = false # doesn't render in platypus
|
45
45
|
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
# Integrate Telegram notifications
|
50
|
+
# For options refer to the source code:
|
51
|
+
# https://github.com/2called-chaos/watchmonkey_cli/blob/master/lib/watchmonkey_cli/hooks/telegram_bot.rb
|
52
|
+
if @argv.delete("--telegram")
|
53
|
+
require "watchmonkey_cli/hooks/telegram_bot"
|
54
|
+
WatchmonkeyCli::TelegramBot.hook!(self, {
|
55
|
+
# to create a bot refer to https://core.telegram.org/bots#6-botfather
|
56
|
+
api_key: "123456789:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
57
|
+
|
58
|
+
# poll timeout, the longer this is (in seconds) the longer it will take to gracefully shut down
|
59
|
+
timeout: 5,
|
60
|
+
|
61
|
+
# optionally log incoming messages
|
62
|
+
#logger: Logger.new(STDOUT),
|
63
|
+
|
64
|
+
# purge old throttle data, default: 30.days
|
65
|
+
#throttle_retention: 30.days,
|
66
|
+
|
67
|
+
# retry sending messages that failed, default: false
|
68
|
+
# Not recommended since on connection failure a HUGE amount of messages will accumulate
|
69
|
+
# and spam you (and reach rate limits) upon connection restore.
|
70
|
+
#retry_on_egress_failure: false,
|
71
|
+
|
72
|
+
# configure your notification targets, if not listed you can't interact with the bot
|
73
|
+
notify: [
|
74
|
+
[
|
75
|
+
# your telegram ID, if you try talking to the bot it will tell you your ID
|
76
|
+
987654321,
|
77
|
+
|
78
|
+
# flags
|
79
|
+
# - :all -- same as :debug, :info, :error (not recommended)
|
80
|
+
# - :debug -- send all debug messages (not recommended)
|
81
|
+
# - :info -- send all info messages (not recommended)
|
82
|
+
# - :error -- send all error messages (RECOMMENDED)
|
83
|
+
# - :admin_flag -- allows access to some commands (/wm_shutdown /stats)
|
84
|
+
[:error, :admin_flag],
|
85
|
+
|
86
|
+
# options (all optional, you can comment them out but leave the {})
|
87
|
+
{
|
88
|
+
# throttle: seconds(int) -- throttle messages by checker uniqid for this long (0/false = no throttle, default)
|
89
|
+
throttle: 15*60,
|
90
|
+
|
91
|
+
# only: Array(string, symbol) -- only notify when tagged with given tags
|
92
|
+
only: %w[production critical],
|
93
|
+
|
94
|
+
# except: Array(string, symbol) -- don't notify when tagged with given tags (runs after only-check)
|
95
|
+
except: %w[database],
|
96
|
+
}
|
97
|
+
],
|
98
|
+
[123456789, [:error], { throttle: 30.minutes }]
|
99
|
+
],
|
100
|
+
})
|
101
|
+
end
|
@@ -59,9 +59,11 @@ module WatchmonkeyCli
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
def initialize app, file
|
62
|
+
def initialize app, file = nil, tags = []
|
63
63
|
@app = app
|
64
64
|
@file = file
|
65
|
+
@tags = tags
|
66
|
+
return unless file
|
65
67
|
begin
|
66
68
|
eval File.read(file, encoding: "utf-8"), binding, file
|
67
69
|
rescue
|
@@ -74,9 +76,42 @@ module WatchmonkeyCli
|
|
74
76
|
@app.fetch_connection(:ssh, name, opts, &b)
|
75
77
|
end
|
76
78
|
|
79
|
+
def tag_all! *tags
|
80
|
+
@tags = tags.map(&:to_sym)
|
81
|
+
end
|
82
|
+
|
77
83
|
def method_missing meth, *args, &block
|
78
84
|
if c = @app.checkers[meth.to_s]
|
79
|
-
|
85
|
+
opts = args.extract_options!
|
86
|
+
only = @app.opts[:tag_only]
|
87
|
+
except = @app.opts[:tag_except]
|
88
|
+
|
89
|
+
# build tags
|
90
|
+
tags = (@tags + (opts[:tags] || []).map(&:to_sym))
|
91
|
+
if @app.opts[:autotag]
|
92
|
+
tags << :"WMC-#{c.class.checker_name}" # checker name
|
93
|
+
if args[0].is_a?(Symbol)
|
94
|
+
tags << :"WMS-#{args[0]}" # ssh/local connection
|
95
|
+
elsif args[0].is_a?(String) && args[0].match(/\Ahttp(s)?:\/\//i)
|
96
|
+
uri = URI.parse(args[0]) rescue false
|
97
|
+
tags << :"WMH-#{uri.hostname.gsub(".", "_")}" if uri # hostname from URL
|
98
|
+
end
|
99
|
+
end
|
100
|
+
tags = tags.uniq
|
101
|
+
@app.tag_list.merge(tags)
|
102
|
+
|
103
|
+
if only.any?
|
104
|
+
if tags.any?{|t| only.include?(t) }
|
105
|
+
if tags.any?{|t| except.include?(t) }
|
106
|
+
return @app.debug "Skipping #{meth} with #{args} and #{opts} due to tag_except filter..."
|
107
|
+
end
|
108
|
+
else
|
109
|
+
return @app.debug "Skipping #{meth} with #{args} and #{opts} due to tag_only filter..."
|
110
|
+
end
|
111
|
+
elsif tags.any?{|t| except.include?(t) }
|
112
|
+
return @app.debug "Skipping #{meth} with #{args} and #{opts} due to tag_except filter..."
|
113
|
+
end
|
114
|
+
c.enqueue(*args, opts.merge(tags: tags))
|
80
115
|
else
|
81
116
|
super
|
82
117
|
end
|
@@ -1,5 +1,26 @@
|
|
1
1
|
# This is a Ruby file!
|
2
2
|
|
3
|
+
# =========================================
|
4
|
+
# = Step 0: Tag all checkers in this file =
|
5
|
+
# =========================================
|
6
|
+
|
7
|
+
# All checkers you define after this will get these
|
8
|
+
# base tags (you can pass additional ones).
|
9
|
+
# Do note that you can call this method multiple
|
10
|
+
# times and it will replace your base tags for
|
11
|
+
# all checkers to follow, base tags will be cleared
|
12
|
+
# upon reaching the end of the file.
|
13
|
+
|
14
|
+
tag_all! :production, :critical
|
15
|
+
|
16
|
+
# You can use additional tags on any checker by passing
|
17
|
+
# a `tags` option consisting of an Array of strings or symbols.
|
18
|
+
# e.g.:
|
19
|
+
# some_checker :some_target, tags: %w[critical foo bar]
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
3
24
|
# =================================
|
4
25
|
# = Step 1: Setup SSH connections =
|
5
26
|
# =================================
|
@@ -36,7 +57,7 @@ ssh_connection :my_server, host: "wheel@example.com", port: 23 # additional opti
|
|
36
57
|
#
|
37
58
|
# timeout Maximum time to wait for request (default: 20 seconds)
|
38
59
|
# verify If enabled the peer will be verified (default: true)
|
39
|
-
# threshold Minimum certificate lifetime before showing warnings (default:
|
60
|
+
# threshold Minimum certificate lifetime before showing warnings (default: 28.days)
|
40
61
|
#
|
41
62
|
ssl_expiration "https://example.com", threshold: 3.months
|
42
63
|
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module WatchmonkeyCli
|
2
2
|
class Application
|
3
3
|
module Core
|
4
|
+
def filtered_threads
|
5
|
+
Thread.list.reject do |thr|
|
6
|
+
thr.backtrace[0]["gems/concurrent-ruby"] rescue false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
4
10
|
# ===================
|
5
11
|
# = Signal trapping =
|
6
12
|
# ===================
|
@@ -37,10 +43,31 @@ module WatchmonkeyCli
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
def fire which, *args
|
46
|
+
def fire which, *args, &block
|
41
47
|
return if @disable_event_firing
|
42
|
-
sync { debug "[Event] Firing #{which} (#{@hooks[which].try(:length) || 0} handlers) #{args.map(&:class)}", 99 }
|
43
|
-
|
48
|
+
sync { debug "[Event] Firing #{which} (#{@hooks[which].try(:length) || 0} handlers) #{args.map(&:class)}#{" HAS_BLOCK" if block}", 99 }
|
49
|
+
if block && (!@hooks[which] || @hooks[which].empty?)
|
50
|
+
block.call
|
51
|
+
else
|
52
|
+
if @hooks[which] && @hooks[which].any?
|
53
|
+
if block
|
54
|
+
catch :abort do
|
55
|
+
_fire_around(@hooks[which], args, 0, &block)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
@hooks[which].all?{|h| h.call(*args) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def _fire_around hooks, args, index = 0, &block
|
65
|
+
return block.call unless hook = hooks[index]
|
66
|
+
skip = catch(:skip) {
|
67
|
+
hook.call(*args) { _fire_around(hooks, args, index + 1, &block) }
|
68
|
+
nil
|
69
|
+
}
|
70
|
+
_fire_around(hooks, args, index + 1, &block) if skip
|
44
71
|
end
|
45
72
|
|
46
73
|
|
@@ -81,7 +108,12 @@ module WatchmonkeyCli
|
|
81
108
|
|
82
109
|
def close_connections!
|
83
110
|
@connections.each do |type, clist|
|
84
|
-
clist.each
|
111
|
+
clist.each do |id, con|
|
112
|
+
if con.established?
|
113
|
+
debug "[SHUTDOWN] closing #{type} connection #{id} #{con}"
|
114
|
+
con.close!
|
115
|
+
end
|
116
|
+
end
|
85
117
|
end
|
86
118
|
end
|
87
119
|
|
@@ -97,8 +129,26 @@ module WatchmonkeyCli
|
|
97
129
|
@queue << [checker, a, ->(*a) {
|
98
130
|
begin
|
99
131
|
result = Checker::Result.new(checker, *a)
|
132
|
+
|
133
|
+
# assign tags
|
134
|
+
taskopts = a.extract_options!
|
135
|
+
result.tags = taskopts[:tags] || []
|
136
|
+
a << taskopts
|
137
|
+
|
100
138
|
checker.debug(result.str_running)
|
101
|
-
checker.
|
139
|
+
checker.rsafe(result) {
|
140
|
+
timeout = checker.class.maxrt.nil? ? @opts[:maxrt] : checker.class.maxrt
|
141
|
+
timeout = timeout.call(self, checker, a) if timeout.respond_to?(:call)
|
142
|
+
begin
|
143
|
+
if timeout && timeout > 0
|
144
|
+
Timeout::timeout(timeout) { cb.call(result, *a) }
|
145
|
+
else
|
146
|
+
cb.call(result, *a)
|
147
|
+
end
|
148
|
+
rescue Timeout::Error => ex
|
149
|
+
result.error! "TIMEOUT: did not finish within #{timeout} seconds, task killed!"
|
150
|
+
end
|
151
|
+
}
|
102
152
|
fire(:result_dump, result, a, checker)
|
103
153
|
result.dump!
|
104
154
|
ensure
|
@@ -3,7 +3,11 @@ module WatchmonkeyCli
|
|
3
3
|
module Dispatch
|
4
4
|
def dispatch action = (@opts[:dispatch] || :help)
|
5
5
|
if respond_to?("dispatch_#{action}")
|
6
|
-
|
6
|
+
fire(:dispatch_before, action)
|
7
|
+
fire(:dispatch_around, action) do
|
8
|
+
send("dispatch_#{action}")
|
9
|
+
end
|
10
|
+
fire(:dispatch_after, action)
|
7
11
|
else
|
8
12
|
abort("unknown action #{action}", 1)
|
9
13
|
end
|
@@ -41,7 +45,7 @@ module WatchmonkeyCli
|
|
41
45
|
ensure
|
42
46
|
@running = false
|
43
47
|
stop_checkers!
|
44
|
-
close_connections!
|
48
|
+
Timeout::timeout(@opts[:conclosewait]) { close_connections! } rescue false
|
45
49
|
release_signals
|
46
50
|
end
|
47
51
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module WatchmonkeyCli
|
2
2
|
class Application
|
3
|
-
attr_reader :opts, :checkers, :connections, :threads, :queue, :hooks, :processed
|
3
|
+
attr_reader :opts, :checkers, :connections, :threads, :queue, :hooks, :processed, :tag_list
|
4
4
|
include Helper
|
5
5
|
include OutputHelper
|
6
6
|
include Colorize
|
@@ -21,9 +21,22 @@ module WatchmonkeyCli
|
|
21
21
|
app.haltpoint
|
22
22
|
rescue Interrupt
|
23
23
|
app.abort("Interrupted", 1)
|
24
|
+
rescue SystemExit
|
25
|
+
# silence
|
24
26
|
ensure
|
27
|
+
$wm_runtime_exiting = true
|
25
28
|
app.fire(:wm_shutdown)
|
26
|
-
app.
|
29
|
+
if app.filtered_threads.length > 1
|
30
|
+
app.error "[WARN] #{app.filtered_threads.length} threads remain (should be 1)..."
|
31
|
+
app.filtered_threads.each do |thr|
|
32
|
+
app.debug "[THR] #{Thread.main == thr ? "MAIN" : "THREAD"}\t#{thr.alive? ? "ALIVE" : "DEAD"}\t#{thr.inspect}", 10
|
33
|
+
thr.backtrace.each do |l|
|
34
|
+
app.debug "[THR]\t#{l}", 20
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
app.debug "1 thread remains..."
|
39
|
+
end
|
27
40
|
end
|
28
41
|
end
|
29
42
|
end
|
@@ -36,6 +49,7 @@ module WatchmonkeyCli
|
|
36
49
|
@monitor = Monitor.new
|
37
50
|
@threads = []
|
38
51
|
@queue = Queue.new
|
52
|
+
@tag_list = Set.new
|
39
53
|
@processed = 0
|
40
54
|
@running = false
|
41
55
|
@opts = {
|
@@ -45,11 +59,16 @@ module WatchmonkeyCli
|
|
45
59
|
colorize: true, # -m flag
|
46
60
|
debug: false, # -d flag
|
47
61
|
threads: 10, # -t flag
|
62
|
+
maxrt: 120.seconds, # max runtime of a single task after which it will be terminated (may break SSH connection), 0/false to not limit runtime
|
63
|
+
conclosewait: 10, # max seconds to wait for connections to be closed (may never if they got killed by maxrt)
|
48
64
|
loop_forever: false, # (internal) loop forever (app mode)
|
49
65
|
loop_wait_empty: 1, # (internal) time to wait in thread if queue is empty
|
66
|
+
autotag: true, # (internal) if true checkers will get auto tags for checker name and hostname/connection
|
50
67
|
silent: false, # -s flag
|
51
68
|
quiet: false, # -q flag
|
52
69
|
stdout: STDOUT, # (internal) STDOUT redirect
|
70
|
+
tag_only: [], # -o flag
|
71
|
+
tag_except: [], # -e flag
|
53
72
|
}
|
54
73
|
init_params
|
55
74
|
yield(self)
|
@@ -63,6 +82,8 @@ module WatchmonkeyCli
|
|
63
82
|
opts.on("--generate-config [myconfig]", "Generates a example config in ~/.watchmonkey") {|s| @opts[:dispatch] = :generate_config; @opts[:config_name] = s }
|
64
83
|
opts.on("-l", "--log [file]", "Log to file, defaults to ~/.watchmonkey/logs/watchmonkey.log") {|s| @opts[:logfile] = s || logger_filename }
|
65
84
|
opts.on("-t", "--threads [NUM]", Integer, "Amount of threads to be used for checking (default: 10)") {|s| @opts[:threads] = s }
|
85
|
+
opts.on("-e", "--except tag1,tag2", Array, "Don't run tasks tagged with given tags") {|s| @opts[:tag_except] = s.map(&:to_sym) }
|
86
|
+
opts.on("-o", "--only tag1,tag2", Array, "Only run tasks tagged with given tags") {|s| @opts[:tag_only] = s.map(&:to_sym) }
|
66
87
|
opts.on("-s", "--silent", "Only print errors and infos") { @opts[:silent] = true }
|
67
88
|
opts.on("-q", "--quiet", "Only print errors") { @opts[:quiet] = true }
|
68
89
|
|
@@ -74,10 +95,15 @@ module WatchmonkeyCli
|
|
74
95
|
opts.on("-z", "Do not check for updates on GitHub (with -v/--version)") { @opts[:check_for_updates] = false }
|
75
96
|
opts.on("--dump-core", "for developers") { @opts[:dump] = true }
|
76
97
|
end
|
98
|
+
fire(:optparse_init, @optparse)
|
77
99
|
end
|
78
100
|
|
79
101
|
def parse_params
|
80
|
-
@optparse
|
102
|
+
fire(:optparse_parse_before, @optparse)
|
103
|
+
fire(:optparse_parse_around, @optparse) do
|
104
|
+
@optparse.parse!(@argv)
|
105
|
+
end
|
106
|
+
fire(:optparse_parse_after, @optparse)
|
81
107
|
rescue OptionParser::ParseError => e
|
82
108
|
abort(e.message)
|
83
109
|
dispatch(:help)
|
@@ -17,6 +17,14 @@ module WatchmonkeyCli
|
|
17
17
|
@checker_name = name
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.maxrt
|
21
|
+
@maxrt
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.maxrt= seconds
|
25
|
+
@maxrt = seconds
|
26
|
+
end
|
27
|
+
|
20
28
|
module AppHelper
|
21
29
|
def init_checkers!
|
22
30
|
@checkers = {}
|
@@ -44,16 +52,24 @@ module WatchmonkeyCli
|
|
44
52
|
|
45
53
|
class Result
|
46
54
|
attr_reader :checker, :type, :args
|
47
|
-
attr_accessor :result, :command, :data
|
55
|
+
attr_accessor :result, :command, :data, :tags
|
48
56
|
|
49
57
|
def initialize checker, *args
|
50
58
|
@checker = checker
|
51
59
|
@args = args
|
52
60
|
@mutex = Monitor.new
|
53
61
|
@type = :info
|
62
|
+
@tags = []
|
54
63
|
@spool = { error: [], info: [], debug: []}
|
55
64
|
end
|
56
65
|
|
66
|
+
def uniqid additional = []
|
67
|
+
([
|
68
|
+
self.class.name,
|
69
|
+
@args.map(&:to_s).to_s,
|
70
|
+
] + additional).join("/")
|
71
|
+
end
|
72
|
+
|
57
73
|
def sync &block
|
58
74
|
@mutex.synchronize(&block)
|
59
75
|
end
|
@@ -147,6 +163,10 @@ module WatchmonkeyCli
|
|
147
163
|
end
|
148
164
|
end
|
149
165
|
|
166
|
+
def blank_config tags = []
|
167
|
+
Application::Configuration.new(app, nil, tags)
|
168
|
+
end
|
169
|
+
|
150
170
|
# def to_s
|
151
171
|
# string = "#<#{self.class.name}:#{self.object_id} "
|
152
172
|
# fields = self.class.inspector_fields.map{|field| "#{field}: #{self.send(field)}"}
|
@@ -168,8 +188,10 @@ module WatchmonkeyCli
|
|
168
188
|
error "#{descriptor}retry #{tries} reason is `#{e.class}: #{e.message}'"
|
169
189
|
e.backtrace.each{|l| debug "\t\t#{l}" }
|
170
190
|
end
|
171
|
-
|
172
|
-
|
191
|
+
unless $wm_runtime_exiting
|
192
|
+
sleep 1
|
193
|
+
retry
|
194
|
+
end
|
173
195
|
end
|
174
196
|
error "#{descriptor}retries exceeded"
|
175
197
|
end
|
@@ -187,10 +209,12 @@ module WatchmonkeyCli
|
|
187
209
|
e.backtrace.each{|l| resultobj.debug "\t\t#{l}" }
|
188
210
|
resultobj.dump!
|
189
211
|
end
|
190
|
-
|
191
|
-
|
212
|
+
unless $wm_runtime_exiting
|
213
|
+
sleep 1
|
214
|
+
retry
|
215
|
+
end
|
192
216
|
end
|
193
|
-
resultobj.error! "
|
217
|
+
resultobj.error! "retries exceeded"
|
194
218
|
resultobj.dump!
|
195
219
|
end
|
196
220
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module WatchmonkeyCli
|
2
|
+
module Checkers
|
3
|
+
class DevPry < Checker
|
4
|
+
self.checker_name = "dev_pry"
|
5
|
+
self.maxrt = false
|
6
|
+
|
7
|
+
def enqueue host, opts = {}
|
8
|
+
host = app.fetch_connection(:loopback, :local) if !host || host == :local
|
9
|
+
host = app.fetch_connection(:ssh, host) if host.is_a?(Symbol)
|
10
|
+
app.enqueue(self, host, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def check! result, host, opts = {}
|
14
|
+
if app.opts[:threads] > 1
|
15
|
+
result.error! "pry only works properly within the main thread, run watchmonkey with `-t0`"
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
require "pry"
|
21
|
+
binding.pry
|
22
|
+
1+1 # pry may bug out otherwise if it's last statement
|
23
|
+
rescue LoadError => ex
|
24
|
+
result.error! "pry is required (gem install pry)! #{ex.class}: #{ex.message}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -4,7 +4,7 @@ module WatchmonkeyCli
|
|
4
4
|
self.checker_name = "ssl_expiration"
|
5
5
|
|
6
6
|
def enqueue page, opts = {}
|
7
|
-
opts = { threshold:
|
7
|
+
opts = { threshold: 28.days, verify: true, timeout: 20 }.merge(opts)
|
8
8
|
app.enqueue(self, page, opts)
|
9
9
|
end
|
10
10
|
|
@@ -35,7 +35,9 @@ module WatchmonkeyCli
|
|
35
35
|
# sec.check!(result, host, opts[which])
|
36
36
|
# end
|
37
37
|
# app.enqueue_sub(self, which, host, opts[which]) if opts[which]
|
38
|
-
spawn_sub(which, host, opts[which].is_a?(Hash) ? opts[which] : {}) unless opts[which] == false
|
38
|
+
#spawn_sub(which, host, (opts[which].is_a?(Hash) ? opts[which] : {}).merge(tags: result.tags)) unless opts[which] == false
|
39
|
+
stags = (opts[:tags] || []).reject{|t| t.to_s.start_with?("WMC-") }
|
40
|
+
blank_config(stags).send(which, host, (opts[which].is_a?(Hash) ? opts[which] : {})) unless opts[which] == false
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -9,7 +9,8 @@ module WatchmonkeyCli
|
|
9
9
|
# if available enable ssl_expiration support
|
10
10
|
if page.start_with?("https://") && opts[:ssl_expiration] != false && !app.running?
|
11
11
|
sopts = { timeout: opts[:timeout] }.merge(opts[:ssl_expiration].is_a?(Hash) ? opts[:ssl_expiration] : {})
|
12
|
-
|
12
|
+
stags = (opts[:tags] || []).reject{|t| t.to_s.start_with?("WMC-") }
|
13
|
+
blank_config(stags).ssl_expiration(page, sopts)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -71,7 +71,7 @@ module WatchmonkeyCli
|
|
71
71
|
<dt>Items in Queue</dt><dd class="qlength">#{@queue.length}</dd>
|
72
72
|
<dt>Items in ReQ</dt><dd class="rqlength">#{@requeue.length}</dd>
|
73
73
|
<dt>Workers</dt><dd class="workers">#{@threads.select{|t| t[:working] }.length}/#{@threads.length} working (#{@threads.select(&:alive?).length} alive)</dd>
|
74
|
-
<dt>Threads</dt><dd class="tlength">#{
|
74
|
+
<dt>Threads</dt><dd class="tlength">#{filtered_threads.length}</dd>
|
75
75
|
<dt>Processed entries</dt><dd class="processed">#{@processed}</dd>
|
76
76
|
<dt>Watching since</dt><dd>#{@boot}</dd>
|
77
77
|
<dt>Last draw</dt><dd class="lastdraw">#{Time.current}</dd>
|
@@ -93,7 +93,7 @@ module WatchmonkeyCli
|
|
93
93
|
$("dd.qlength").html("#{@queue.length}");
|
94
94
|
$("dd.rqlength").html("#{@requeue.length}");
|
95
95
|
$("dd.workers").html("#{@threads.select{|t| t[:working] }.length}/#{@threads.length} working#{ti}");
|
96
|
-
$("dd.tlength").html("#{
|
96
|
+
$("dd.tlength").html("#{filtered_threads.length}");
|
97
97
|
$("dd.processed").html("#{@processed}");
|
98
98
|
$("dd.lastdraw").html("#{Time.current}");
|
99
99
|
$("pre.lasterrors").html("#{escape_javascript @platypus_status_cache[:errors].map{|t,e| "#{t}: #{e}" }.join("\n")}");
|
@@ -37,11 +37,12 @@ module WatchmonkeyCli
|
|
37
37
|
puts " Queue: #{@queue.length}"
|
38
38
|
puts " Requeue: #{@requeue.length}"
|
39
39
|
puts " Workers: #{@threads.select{|t| t[:working] }.length}/#{@threads.length} working (#{@threads.select(&:alive?).length} alive)"
|
40
|
-
puts " Threads: #{
|
40
|
+
puts " Threads: #{filtered_threads.length}"
|
41
41
|
# puts " #{@threads.select(&:alive?).length} alive"
|
42
42
|
# puts " #{@threads.select{|t| t.status == "run" }.length} running"
|
43
43
|
# puts " #{@threads.select{|t| t.status == "sleep" }.length} sleeping"
|
44
44
|
puts " Processed: #{@processed}"
|
45
|
+
puts " Promises: #{@telegram_bot_egress_promises.length}" if @telegram_bot_egress_promises
|
45
46
|
puts "========== //STATUS =========="
|
46
47
|
end
|
47
48
|
end
|
@@ -101,6 +102,16 @@ module WatchmonkeyCli
|
|
101
102
|
}]
|
102
103
|
end
|
103
104
|
end
|
105
|
+
|
106
|
+
def requeue_runall
|
107
|
+
return if $wm_runtime_exiting
|
108
|
+
sync do
|
109
|
+
debug "Running all queued tasks immediately!"
|
110
|
+
@requeue.each_with_index do |(run_at, callback), index|
|
111
|
+
@requeue[index][0] = Time.now
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
104
115
|
end
|
105
116
|
end
|
106
117
|
end
|