sensu-plugins-consul-magec 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +161 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/bin/check-consul-failures.rb +103 -0
- data/bin/check-consul-kv-ttl.rb +176 -0
- data/bin/check-consul-leader.rb +129 -0
- data/bin/check-consul-maintenance.rb +98 -0
- data/bin/check-consul-members.rb +136 -0
- data/bin/check-consul-quorum.rb +91 -0
- data/bin/check-consul-servers.rb +118 -0
- data/bin/check-consul-service-health.rb +161 -0
- data/bin/check-consul-stale-peers.rb +73 -0
- data/bin/check-service-consul.rb +160 -0
- data/lib/sensu-plugins-consul.rb +3 -0
- data/lib/sensu-plugins-consul/check/base.rb +112 -0
- data/lib/sensu-plugins-consul/version.rb +11 -0
- metadata +246 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-consul-servers
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# This plugin checks if consul is up and reachable. It then checks
|
9
|
+
# the number of peers matches the excepted value
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# plain text
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: diplomat
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# #YELLOW
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
#
|
26
|
+
# LICENSE:
|
27
|
+
# Copyright 2015 Sonian, Inc. and contributors. <support@sensuapp.org>
|
28
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
29
|
+
# for details.
|
30
|
+
#
|
31
|
+
|
32
|
+
require 'sensu-plugin/check/cli'
|
33
|
+
require 'rest-client'
|
34
|
+
require 'json'
|
35
|
+
|
36
|
+
#
|
37
|
+
# Consul Status
|
38
|
+
#
|
39
|
+
class ConsulStatus < Sensu::Plugin::Check::CLI
|
40
|
+
option :server,
|
41
|
+
description: 'consul server',
|
42
|
+
short: '-s SERVER',
|
43
|
+
long: '--server SERVER',
|
44
|
+
default: '127.0.0.1'
|
45
|
+
|
46
|
+
option :port,
|
47
|
+
description: 'consul http port',
|
48
|
+
short: '-p PORT',
|
49
|
+
long: '--port PORT',
|
50
|
+
default: '8500'
|
51
|
+
|
52
|
+
option :min,
|
53
|
+
description: 'minimum number of peers',
|
54
|
+
short: '-g GREATER THAN',
|
55
|
+
long: '--greater GREATER THAN',
|
56
|
+
proc: proc(&:to_i),
|
57
|
+
default: 3
|
58
|
+
|
59
|
+
option :expected,
|
60
|
+
description: 'expected number of peers',
|
61
|
+
short: '-e EXPECT',
|
62
|
+
long: '--expect EXPECT',
|
63
|
+
proc: proc(&:to_i),
|
64
|
+
default: 5
|
65
|
+
|
66
|
+
option :scheme,
|
67
|
+
description: 'consul listener scheme',
|
68
|
+
short: '-S SCHEME',
|
69
|
+
long: '--scheme SCHEME',
|
70
|
+
default: 'http'
|
71
|
+
|
72
|
+
option :insecure,
|
73
|
+
description: 'if set, disables SSL verification',
|
74
|
+
short: '-k',
|
75
|
+
long: '--insecure',
|
76
|
+
boolean: true,
|
77
|
+
default: false
|
78
|
+
|
79
|
+
option :capath,
|
80
|
+
description: 'absolute path to an alternate CA file',
|
81
|
+
short: '-c CAPATH',
|
82
|
+
long: '--capath CAPATH'
|
83
|
+
|
84
|
+
option :timeout,
|
85
|
+
description: 'connection will time out after this many seconds',
|
86
|
+
short: '-t TIMEOUT_IN_SECONDS',
|
87
|
+
long: '--timeout TIMEOUT_IN_SECONDS',
|
88
|
+
proc: proc(&:to_i),
|
89
|
+
default: 5
|
90
|
+
|
91
|
+
option :token,
|
92
|
+
description: 'ACL token',
|
93
|
+
long: '--token ACL_TOKEN'
|
94
|
+
|
95
|
+
def run
|
96
|
+
url = "#{config[:scheme]}://#{config[:server]}:#{config[:port]}/v1/status/peers"
|
97
|
+
options = { timeout: config[:timeout],
|
98
|
+
verify_ssl: (OpenSSL::SSL::VERIFY_NONE if defined? config[:insecure]),
|
99
|
+
ssl_ca_file: (config[:capath] if defined? config[:capath]),
|
100
|
+
headers: { 'X-Consul-Token' => config[:token] } }
|
101
|
+
|
102
|
+
json = RestClient::Resource.new(url, options).get
|
103
|
+
peers = JSON.parse(json).length.to_i
|
104
|
+
if peers < config[:min]
|
105
|
+
critical "[#{peers}] peers is below critical threshold of [#{config[:min]}]"
|
106
|
+
elsif peers != config[:expected]
|
107
|
+
warning "[#{peers}] peers is outside of expected count of [#{config[:expected]}]"
|
108
|
+
else
|
109
|
+
ok 'Peers within threshold'
|
110
|
+
end
|
111
|
+
rescue Errno::ECONNREFUSED
|
112
|
+
critical 'Consul is not responding'
|
113
|
+
rescue RestClient::RequestTimeout
|
114
|
+
critical 'Consul Connection timed out'
|
115
|
+
rescue RestClient::Exception => e
|
116
|
+
unknown "Consul returned: #{e}"
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-consul-service-health
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# This plugin assists in checking the check status of a Consul Service
|
9
|
+
# In addition, it provides additional Yieldbot logic for Output containing
|
10
|
+
# JSON.
|
11
|
+
#
|
12
|
+
# OUTPUT:
|
13
|
+
# plain text
|
14
|
+
#
|
15
|
+
# PLATFORMS:
|
16
|
+
# Linux
|
17
|
+
#
|
18
|
+
# DEPENDENCIES:
|
19
|
+
# gem: sensu-plugin
|
20
|
+
# gem: diplomat
|
21
|
+
#
|
22
|
+
# USAGE:
|
23
|
+
# ./check-consul-service-health -s influxdb
|
24
|
+
# ./check-consul-service-health -a
|
25
|
+
#
|
26
|
+
# NOTES:
|
27
|
+
#
|
28
|
+
# LICENSE:
|
29
|
+
# Copyright 2015 Yieldbot, Inc. <devops@yieldbot.com>
|
30
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
31
|
+
# for details.
|
32
|
+
#
|
33
|
+
|
34
|
+
require 'sensu-plugin/check/cli'
|
35
|
+
require 'diplomat'
|
36
|
+
require 'json'
|
37
|
+
|
38
|
+
#
|
39
|
+
# Service Status
|
40
|
+
#
|
41
|
+
class CheckConsulServiceHealth < Sensu::Plugin::Check::CLI
|
42
|
+
option :consul,
|
43
|
+
description: 'consul server',
|
44
|
+
long: '--consul SERVER',
|
45
|
+
default: 'http://localhost:8500'
|
46
|
+
|
47
|
+
option :nodename,
|
48
|
+
description: 'check all consul services running on the specified node',
|
49
|
+
short: '-n NODENAME',
|
50
|
+
long: '--node NODENAME'
|
51
|
+
|
52
|
+
option :service,
|
53
|
+
description: 'a service managed by consul',
|
54
|
+
short: '-s SERVICE',
|
55
|
+
long: '--service SERVICE',
|
56
|
+
default: 'consul'
|
57
|
+
|
58
|
+
option :tags,
|
59
|
+
description: 'filter services by a comma-separated list of tags (requires --service)',
|
60
|
+
short: '-t TAGS',
|
61
|
+
long: '--tags TAGS'
|
62
|
+
|
63
|
+
option :all,
|
64
|
+
description: 'get all services (not compatible with --tags)',
|
65
|
+
short: '-a',
|
66
|
+
long: '--all'
|
67
|
+
|
68
|
+
option :fail_if_not_found,
|
69
|
+
description: 'fail if no service is found',
|
70
|
+
short: '-f',
|
71
|
+
long: '--fail-if-not-found'
|
72
|
+
|
73
|
+
option :token,
|
74
|
+
description: 'ACL token',
|
75
|
+
long: '--token ACL_TOKEN'
|
76
|
+
|
77
|
+
# Get the service checks for the given service
|
78
|
+
def acquire_service_data
|
79
|
+
if config[:tags] && config[:service]
|
80
|
+
tags = config[:tags].split(',').to_set
|
81
|
+
services = []
|
82
|
+
Diplomat::Health.service(config[:service]).each do |s|
|
83
|
+
if s['Service']['Tags'].to_set.superset? tags
|
84
|
+
services.push(*s['Checks'])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
services
|
88
|
+
elsif config[:nodename]
|
89
|
+
data = []
|
90
|
+
begin
|
91
|
+
services = Diplomat::Node.get(config[:nodename]).Services
|
92
|
+
rescue StandardError
|
93
|
+
services = {}
|
94
|
+
end
|
95
|
+
services.each_value do |service|
|
96
|
+
Diplomat::Health.checks(service['Service']).each do |check|
|
97
|
+
data.push(check) if check.Node == config[:nodename]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
data
|
101
|
+
elsif config[:all]
|
102
|
+
Diplomat::Health.state('any')
|
103
|
+
else
|
104
|
+
Diplomat::Health.checks(config[:service])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Do work
|
109
|
+
def run
|
110
|
+
if config[:tags] && config[:all]
|
111
|
+
critical 'Cannot specify --tags and --all simultaneously (Consul health/service/ versus health/state/).'
|
112
|
+
end
|
113
|
+
|
114
|
+
Diplomat.configure do |dc|
|
115
|
+
dc.url = config[:consul]
|
116
|
+
dc.acl_token = config[:token]
|
117
|
+
headers = {}
|
118
|
+
headers['X-Consul-Token'] = config[:token] if config[:token]
|
119
|
+
dc.options = {
|
120
|
+
headers: headers
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
found = false
|
125
|
+
warnings = false
|
126
|
+
criticals = false
|
127
|
+
checks = []
|
128
|
+
|
129
|
+
# Process all of the nonpassing service checks
|
130
|
+
acquire_service_data.each do |d|
|
131
|
+
found = true
|
132
|
+
checkId = d['CheckID'] # rubocop:disable Style/VariableName
|
133
|
+
checkStatus = d['Status'] # rubocop:disable Style/VariableName
|
134
|
+
|
135
|
+
# If we are passing do nothing
|
136
|
+
next if checkStatus == 'passing'
|
137
|
+
|
138
|
+
checks.push(
|
139
|
+
checkId => d['Output'],
|
140
|
+
'Status' => checkStatus
|
141
|
+
)
|
142
|
+
|
143
|
+
warnings = true if %w[warning].include? checkStatus
|
144
|
+
criticals = true if %w[critical unknown].include? checkStatus
|
145
|
+
end
|
146
|
+
|
147
|
+
if config[:fail_if_not_found] && !found
|
148
|
+
msg = 'Could not find checks for any services'
|
149
|
+
if config[:service]
|
150
|
+
msg = "Could not find checks for service #{config[:service]}"
|
151
|
+
if config[:tags]
|
152
|
+
msg += " with tags #{config[:tags]}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
critical msg
|
156
|
+
end
|
157
|
+
critical checks if criticals
|
158
|
+
warning checks if warnings
|
159
|
+
ok
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-consul-stale-peers
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# This plugin checks the raft configuration for stale ("unknown") peers.
|
9
|
+
#
|
10
|
+
# OUTPUT:
|
11
|
+
# plain text
|
12
|
+
#
|
13
|
+
# PLATFORMS:
|
14
|
+
# Linux
|
15
|
+
#
|
16
|
+
# DEPENDENCIES:
|
17
|
+
# gem: sensu-plugin
|
18
|
+
# gem: rest-client
|
19
|
+
#
|
20
|
+
# USAGE:
|
21
|
+
# Connect to localhost, go critical when there are any stale peers:
|
22
|
+
# ./check-consul-stale-peers
|
23
|
+
#
|
24
|
+
# Connect to a remote Consul server over HTTPS:
|
25
|
+
# ./check-consul-stale-peers -s 192.168.42.42 -p 4443 -P https
|
26
|
+
#
|
27
|
+
# Go critical when the cluster has two or more stale peers:
|
28
|
+
# ./check-consul-stale-peers -W 1 -C 2
|
29
|
+
#
|
30
|
+
# NOTES:
|
31
|
+
#
|
32
|
+
# LICENSE:
|
33
|
+
# Copyright 2018, Jonathan Hartman <j@hartman.io>
|
34
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
35
|
+
# for details.
|
36
|
+
#
|
37
|
+
|
38
|
+
require 'sensu-plugins-consul/check/base'
|
39
|
+
|
40
|
+
#
|
41
|
+
# Consul stale peers
|
42
|
+
#
|
43
|
+
class ConsulStalePeers < SensuPluginsConsul::Check::Base
|
44
|
+
option :warning,
|
45
|
+
description: 'Warn when there are this many stale peers',
|
46
|
+
short: '-W NUMBER_OF_PEERS',
|
47
|
+
long: '--warning NUMBER_OF_PEERS',
|
48
|
+
proc: proc(&:to_i),
|
49
|
+
default: 1
|
50
|
+
|
51
|
+
option :critical,
|
52
|
+
description: 'Go critical when there are this many stale peers',
|
53
|
+
short: '-C NUMBER_OF_PEERS',
|
54
|
+
long: '--critical NUMBER_OF_PEERS',
|
55
|
+
proc: proc(&:to_i),
|
56
|
+
default: 1
|
57
|
+
|
58
|
+
def run
|
59
|
+
raft = consul_get('operator/raft/configuration')
|
60
|
+
|
61
|
+
res = raft['Servers'].select { |s| s['Node'] == '(unknown)' }.length
|
62
|
+
|
63
|
+
msg = "Cluster contains #{res} stale peer#{'s' unless res == 1}"
|
64
|
+
|
65
|
+
if res >= config[:critical]
|
66
|
+
critical msg
|
67
|
+
elsif res >= config[:warning]
|
68
|
+
warning msg
|
69
|
+
else
|
70
|
+
ok msg
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-service-consul
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# This plugin checks if consul says a service is 'passing' or
|
9
|
+
# 'critical'
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# plain text
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
# gem: diplomat
|
20
|
+
#
|
21
|
+
# USAGE:
|
22
|
+
# ./check-service-consul -s influxdb
|
23
|
+
# ./check-service-consul -a
|
24
|
+
#
|
25
|
+
# NOTES:
|
26
|
+
#
|
27
|
+
# LICENSE:
|
28
|
+
# Copyright 2015 Yieldbot, Inc. <Sensu-Plugins>
|
29
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
30
|
+
# for details.
|
31
|
+
#
|
32
|
+
|
33
|
+
require 'sensu-plugin/check/cli'
|
34
|
+
require 'diplomat'
|
35
|
+
|
36
|
+
#
|
37
|
+
# Service Status
|
38
|
+
#
|
39
|
+
class ServiceStatus < Sensu::Plugin::Check::CLI
|
40
|
+
option :consul,
|
41
|
+
description: 'consul server',
|
42
|
+
long: '--consul SERVER',
|
43
|
+
default: 'http://localhost:8500'
|
44
|
+
|
45
|
+
option :service,
|
46
|
+
description: 'a service managed by consul',
|
47
|
+
short: '-s SERVICE',
|
48
|
+
long: '--service SERVICE',
|
49
|
+
default: 'consul'
|
50
|
+
|
51
|
+
option :tags,
|
52
|
+
description: 'filter services by a comma-separated list of tags (requires --service)',
|
53
|
+
short: '-t TAGS',
|
54
|
+
long: '--tags TAGS'
|
55
|
+
|
56
|
+
option :all,
|
57
|
+
description: 'get all services in a non-passing status (not compatible with --tags)',
|
58
|
+
short: '-a',
|
59
|
+
long: '--all'
|
60
|
+
|
61
|
+
option :fail_if_not_found,
|
62
|
+
description: 'fail if no service is found',
|
63
|
+
short: '-f',
|
64
|
+
long: '--fail-if-not-found'
|
65
|
+
|
66
|
+
option :token,
|
67
|
+
description: 'ACL token',
|
68
|
+
long: '--token ACL_TOKEN'
|
69
|
+
|
70
|
+
# Get the check data for the service from consul
|
71
|
+
#
|
72
|
+
def acquire_service_data
|
73
|
+
if config[:tags] && config[:service]
|
74
|
+
tags = config[:tags].split(',').to_set
|
75
|
+
services = []
|
76
|
+
Diplomat::Health.service(config[:service]).each do |s|
|
77
|
+
if s['Service']['Tags'].to_set.superset? tags
|
78
|
+
services.push(*s['Checks'])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
return services
|
82
|
+
elsif config[:all]
|
83
|
+
Diplomat::Health.state('any')
|
84
|
+
else
|
85
|
+
Diplomat::Health.checks(config[:service])
|
86
|
+
end
|
87
|
+
rescue Faraday::ConnectionFailed => e
|
88
|
+
warning "Connection error occurred: #{e}"
|
89
|
+
rescue Diplomat::UnknownStatus => e
|
90
|
+
if e.message.include?('403')
|
91
|
+
critical %(ACL token is not authorized to access service "#{config[:service]}")
|
92
|
+
else
|
93
|
+
critical "Unhandled exception(#{e.class}) -- #{e.message}"
|
94
|
+
end
|
95
|
+
rescue Faraday::ClientError => e
|
96
|
+
if e.response[:status] == 403
|
97
|
+
critical %(ACL token is not authorized to access service "#{config[:service]}": #{e.response[:body]})
|
98
|
+
else
|
99
|
+
unknown "Exception occurred when checking consul service: #{e}"
|
100
|
+
end
|
101
|
+
rescue StandardError => e
|
102
|
+
unknown "Exception occurred when checking consul service: #{e}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Main function
|
106
|
+
#
|
107
|
+
def run
|
108
|
+
if config[:tags] && config[:all]
|
109
|
+
critical 'Cannot specify --tags and --all simultaneously (Consul health/service/ versus health/state/).'
|
110
|
+
end
|
111
|
+
|
112
|
+
Diplomat.configure do |dc|
|
113
|
+
dc.url = config[:consul]
|
114
|
+
dc.acl_token = config[:token]
|
115
|
+
dc.options = {
|
116
|
+
headers: {
|
117
|
+
'X-Consul-Token' => config[:token]
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
data = acquire_service_data
|
123
|
+
passing = []
|
124
|
+
failing = []
|
125
|
+
data.each do |d|
|
126
|
+
if d['Status'] == 'passing'
|
127
|
+
passing << {
|
128
|
+
'node' => d['Node'],
|
129
|
+
'service' => d['ServiceName'],
|
130
|
+
'service_id' => d['ServiceID'],
|
131
|
+
'notes' => d['Notes']
|
132
|
+
}
|
133
|
+
elsif d['Status'] == 'critical'
|
134
|
+
failing << {
|
135
|
+
'node' => d['Node'],
|
136
|
+
'service' => d['ServiceName'],
|
137
|
+
'service_id' => d['ServiceID'],
|
138
|
+
'notes' => d['Notes']
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if failing.empty? && passing.empty?
|
144
|
+
msg = 'Could not find checks for any services'
|
145
|
+
if config[:service]
|
146
|
+
msg = "Could not find checks for service #{config[:service]}"
|
147
|
+
if config[:tags]
|
148
|
+
msg += " with tags #{config[:tags]}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
if config[:fail_if_not_found]
|
152
|
+
critical msg
|
153
|
+
else
|
154
|
+
unknown msg
|
155
|
+
end
|
156
|
+
end
|
157
|
+
critical failing unless failing.empty?
|
158
|
+
ok passing unless passing.empty?
|
159
|
+
end
|
160
|
+
end
|