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.
- 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
|