sensu-plugins-docker-swarm 3.3.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,93 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-container
4
+ #
5
+ # DESCRIPTION:
6
+ # This is a simple check script for Sensu to check that a Docker container is
7
+ # running. You can pass in either a container id or a container name.
8
+ #
9
+ # OUTPUT:
10
+ # plain text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ #
18
+ # USAGE:
19
+ # check-container.rb -H /var/run/docker.sock -N c92d402a5d14
20
+ # CheckDockerContainer OK: c92d402a5d14 is running on /var/run/docker.sock.
21
+ #
22
+ # check-container.rb -H https://127.0.0.1:2376 -N circle_burglar
23
+ # CheckDockerContainer CRITICAL: circle_burglar is not running on https://127.0.0.1:2376
24
+ #
25
+ # NOTES:
26
+ # => State.running == true -> OK
27
+ # => State.running == false -> CRITICAL
28
+ # => Not Found -> CRITICAL
29
+ # => Can't connect to Docker -> WARNING
30
+ # => Other exception -> WARNING
31
+ #
32
+ # LICENSE:
33
+ # Copyright 2014 Sonian, Inc. and contributors. <support@sensuapp.org>
34
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
35
+ # for details.
36
+ #
37
+
38
+ require 'sensu-plugin/check/cli'
39
+ require 'sensu-plugins-docker/client_helpers'
40
+
41
+ #
42
+ # Check Docker Container
43
+ #
44
+ class CheckDockerContainer < Sensu::Plugin::Check::CLI
45
+ option :docker_host,
46
+ short: '-H DOCKER_HOST',
47
+ long: '--docker-host DOCKER_HOST',
48
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path'
49
+
50
+ option :container,
51
+ short: '-N CONTAINER',
52
+ long: '--container-name CONTAINER',
53
+ required: true
54
+
55
+ option :tag,
56
+ short: '-t TAG',
57
+ long: '--tag TAG'
58
+
59
+ option :allowexited,
60
+ short: '-x',
61
+ long: '--allow-exited',
62
+ boolean: true,
63
+ description: 'Do not raise alert if container has exited without error'
64
+
65
+ def run
66
+ @client = DockerApi.new(config[:docker_host])
67
+ path = "/containers/#{config[:container]}/json"
68
+ response = @client.call(path, false)
69
+ if response.code.to_i == 404
70
+ critical "Container #{config[:container]} is not running on #{@client.uri}"
71
+ end
72
+ body = parse_json(response)
73
+ container_running = body['State']['Running']
74
+ if container_running
75
+ if config[:tag]
76
+ image = body['Config']['Image']
77
+ match = image.match(/^(?:([^\/]+)\/)?(?:([^\/]+)\/)?([^@:\/]+)(?:[@:](.+))?$/)
78
+ unless match && match[4] == config[:tag]
79
+ critical "#{config[:container]}'s tag is '#{match[4]}', especting '#{config[:tag]}'"
80
+ end
81
+ end
82
+ ok "#{config[:container]} is running on #{@client.uri}."
83
+ elsif config[:allowexited] && body['State']['Status'] == 'exited'
84
+ if (body['State']['ExitCode']).zero?
85
+ ok "#{config[:container]} has exited without error on #{@client.uri}."
86
+ else
87
+ critical "#{config[:container]} has exited with status code #{body['State']['ExitCode']} on #{@client.uri}."
88
+ end
89
+ else
90
+ critical "#{config[:container]} is #{body['State']['Status']} on #{@client.uri}."
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,108 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-docker-container
4
+ #
5
+ # DESCRIPTION:
6
+ # This is a simple check script for Sensu to check the number of a Docker Container
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: net_http_unix
17
+ #
18
+ # USAGE:
19
+ # check-docker-container.rb -w 3 -c 3
20
+ # => 1 container running = OK.
21
+ # => 4 container running = CRITICAL
22
+ #
23
+ # check-docker-container.rb -H /var/run/docker.sock -w 3 -c 3
24
+ # => 1 container running = OK.
25
+ # => 4 container running = CRITICAL
26
+ #
27
+ # check-docker-container.rb -H https://127.0.0.1:2376 -w 3 -c 3
28
+ # => 1 container running = OK.
29
+ # => 4 container running = CRITICAL
30
+ #
31
+ # NOTES:
32
+ #
33
+ # LICENSE:
34
+ # Author Yohei Kawahara <inokara@gmail.com>
35
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
36
+ # for details.
37
+ #
38
+
39
+ require 'sensu-plugin/check/cli'
40
+ require 'sensu-plugins-docker/client_helpers'
41
+
42
+ #
43
+ # Check Docker Containers
44
+ #
45
+ class CheckDockerContainers < Sensu::Plugin::Check::CLI
46
+ option :docker_host,
47
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
48
+ short: '-H DOCKER_HOST',
49
+ long: '--docker-host DOCKER_HOST'
50
+
51
+ option :warn_over,
52
+ short: '-W N',
53
+ long: '--warn-over N',
54
+ description: 'Trigger a warning if over a number',
55
+ proc: proc(&:to_i)
56
+
57
+ option :crit_over,
58
+ short: '-C N',
59
+ long: '--critical-over N',
60
+ description: 'Trigger a critical if over a number',
61
+ proc: proc(&:to_i)
62
+
63
+ option :warn_under,
64
+ short: '-w N',
65
+ long: '--warn-under N',
66
+ description: 'Trigger a warning if under a number',
67
+ proc: proc(&:to_i),
68
+ default: 1
69
+
70
+ option :crit_under,
71
+ short: '-c N',
72
+ long: '--critical-under N',
73
+ description: 'Trigger a critical if under a number',
74
+ proc: proc(&:to_i),
75
+ default: 1
76
+
77
+ def under_message(crit_under, count)
78
+ "Less than #{crit_under} containers running. #{count} running."
79
+ end
80
+
81
+ def over_message(crit_over, count)
82
+ "More than #{crit_over} containers running. #{count} running."
83
+ end
84
+
85
+ def evaluate_count(count)
86
+ # #YELLOW
87
+ if config.key?(:crit_under) && count < config[:crit_under]
88
+ critical under_message(config[:crit_under], count)
89
+ # #YELLOW
90
+ elsif config.key?(:crit_over) && count > config[:crit_over]
91
+ critical over_message(config[:crit_over], count)
92
+ # #YELLOW
93
+ elsif config.key?(:warn_under) && count < config[:warn_under]
94
+ warning under_message(config[:warn_under], count)
95
+ # #YELLOW
96
+ elsif config.key?(:warn_over) && count > config[:warn_over]
97
+ warning over_message(config[:warn_over], count)
98
+ else
99
+ ok
100
+ end
101
+ end
102
+
103
+ def run
104
+ @client = DockerApi.new(config[:docker_host])
105
+ containers = @client.parse('/containers/json')
106
+ evaluate_count containers.size
107
+ end
108
+ end
@@ -0,0 +1,199 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-service-logs
4
+ #
5
+ # DESCRIPTION:
6
+ # Checks docker logs for specified strings
7
+ # with the option to ignore lines if they contain specified substrings.
8
+ #
9
+ # OUTPUT:
10
+ # plain text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ # gem: net_http_unix
18
+ #
19
+ # USAGE:
20
+ # # Check only one service
21
+ # check-service-logs.rb -H /tmp/docker.sock -N logspout -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
22
+ # => 1 service running = OK
23
+ # => 4 service running = CRITICAL
24
+ #
25
+ # # Check multiple services
26
+ # check-service-logs.rb -H /tmp/docker.sock -N logspout -N logtest -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
27
+ # => 1 service running = OK
28
+ # => 4 service running = CRITICAL
29
+ #
30
+ # # Check all services
31
+ # check-service-logs.rb -H /tmp/docker.sock -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
32
+ # => 1 services running = OK
33
+ # => 4 services running = CRITICAL
34
+ #
35
+ # NOTES:
36
+ # The API parameter required to use the limited lookback (-t) was introduced
37
+ # the Docker server API version 1.19. This check may still work on older API
38
+ # versions if you don't want to limit the timestamps of logs.
39
+ #
40
+ # LICENSE:
41
+ # Author: Nathan Newman <newmannh@gmail.com>, Kel Cecil <kelcecil@praisechaos.com>
42
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
43
+ # for details.
44
+ #
45
+
46
+ require 'sensu-plugin/check/cli'
47
+ require 'sensu-plugins-docker/client_helpers'
48
+
49
+ class ServiceLogChecker < Sensu::Plugin::Check::CLI
50
+ option :docker_host,
51
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
52
+ short: '-H DOCKER_HOST',
53
+ long: '--docker-host DOCKER_HOST'
54
+
55
+ option :service,
56
+ description: 'name of service; can be used multiple times. /!\ All running services will be check if this options is not provided',
57
+ short: '-N service',
58
+ long: '--service-name service',
59
+ default: [],
60
+ proc: proc { |flag| (@options[:service][:accumulated] ||= []).push(flag) }
61
+
62
+ option :red_flags,
63
+ description: 'String whose presence (case-insensitive by default) in a log line indicates an error; can be used multiple times',
64
+ short: '-r ERR_STRING',
65
+ long: '--red-flag ERR_STRING',
66
+ default: [],
67
+ proc: proc { |flag| (@options[:red_flags][:accumulated] ||= []).push(flag) }
68
+
69
+ option :ignore_list,
70
+ description: 'String whose presence (case-insensitive by default) in a log line indicates the line should be ignored; can be used multiple times',
71
+ short: '-i IGNSTR',
72
+ long: '--ignore-lines-with IGNSTR',
73
+ default: [],
74
+ proc: proc { |flag| (@options[:ignore_list][:accumulated] ||= []).push(flag) }
75
+
76
+ option :case_sensitive,
77
+ description: 'indicates all red_flag and ignore_list substring matching should be case-sensitive instead of the default case-insensitive',
78
+ short: '-c',
79
+ long: '--case-sensitive',
80
+ boolean: true
81
+
82
+ option :hours_ago,
83
+ description: 'Amount of time in hours to look back for log strings',
84
+ short: '-t HOURS',
85
+ long: '--hours-ago HOURS',
86
+ required: false
87
+
88
+ option :seconds_ago,
89
+ description: 'Amount of time in seconds to look back for log strings',
90
+ short: '-s SECONDS',
91
+ long: '--seconds-ago SECONDS',
92
+ required: false
93
+
94
+ option :check_all,
95
+ description: 'If all services are checked (no service name provided with -n) , check offline services too',
96
+ short: '-a',
97
+ long: '--all',
98
+ default: false,
99
+ boolean: true
100
+
101
+ option :disable_stdout,
102
+ description: 'Disable the check on STDOUT logs. By default both STDERR and STDOUT are checked',
103
+ short: '-1',
104
+ long: '--no-stdout',
105
+ default: true,
106
+ boolean: true,
107
+ proc: proc { false } # used to negate the false(default)->true boolean option behaviour to true(default)->false
108
+
109
+ option :disable_stderr,
110
+ description: 'Disable the check on STDERR logs. By default both STDERR and STDOUT are checked',
111
+ short: '-2',
112
+ long: '--no-stderr',
113
+ default: true,
114
+ boolean: true,
115
+ proc: proc { false } # used to negate the false(default)->true boolean option behaviour to true(default)->false
116
+
117
+ def calculate_timestamp(seconds_ago = nil)
118
+ seconds_ago = yield if block_given?
119
+ (Time.now - seconds_ago).to_i
120
+ end
121
+
122
+ def process_docker_logs(service_name)
123
+ path = "/services/#{service_name}/logs?stdout=#{config[:disable_stdout]}&stderr=#{config[:disable_stderr]}&timestamps=true"
124
+ if config.key? :hours_ago
125
+ timestamp = calculate_timestamp { config[:hours_ago].to_i * 3600 }
126
+ elsif config.key? :seconds_ago
127
+ timestamp = calculate_timestamp config[:seconds_ago].to_i
128
+ end
129
+ path = "#{path}&since=#{timestamp}"
130
+ response = @client.call(path, false)
131
+ if response.code.to_i == 404
132
+ critical "service '#{service_name}' not found on #{@client.uri}"
133
+ end
134
+ yield remove_headers response.read_body
135
+ end
136
+
137
+ def remove_headers(raw_logs)
138
+ lines = raw_logs.split("\n")
139
+ lines.map! do |line|
140
+ # Check only logs generated with the 8 bits control
141
+ if !line.nil? && line.bytesize > 8 && /^(0|1|2)000$/ =~ line.byteslice(0, 4).unpack('C*').join('')
142
+ # Remove the first 8 bits and ansii colors too
143
+ line.byteslice(8, line.bytesize).gsub(/\x1b\[[\d;]*?m/, '')
144
+ end
145
+ end
146
+ # We want the most recent logs lines first
147
+ lines.compact.reverse.join("\n")
148
+ end
149
+
150
+ def includes_any?(str, array_of_substrings)
151
+ array_of_substrings.each do |substring|
152
+ return true if str.include? substring
153
+ end
154
+ false
155
+ end
156
+
157
+ def detect_problem(logs)
158
+ whiteflags = config[:ignore_list]
159
+ redflags = config[:red_flags]
160
+ unless config[:case_sensitive]
161
+ logs = logs.downcase
162
+ whiteflags.map!(&:downcase)
163
+ redflags.map!(&:downcase)
164
+ end
165
+
166
+ logs.split("\n").each do |line|
167
+ return line if !includes_any?(line, whiteflags) && includes_any?(line, redflags)
168
+ end
169
+ nil
170
+ end
171
+
172
+ def run
173
+ @client = DockerApi.new(config[:docker_host])
174
+ problem = []
175
+ problem_string = nil
176
+ path = "/services/json?all=#{config[:check_all]}"
177
+ services = config[:service]
178
+ if config[:service].none?
179
+ warn_msg = %(
180
+ Collecting logs from all services is dangerous and could lead to sensu client hanging depending on volume of logs.
181
+ This not recommended for production environments.
182
+ ).gsub(/\s+/, ' ').strip
183
+ message warn_msg
184
+ end
185
+ services = @client.parse(path).map { |p| p['Names'][0].delete('/') } if services.none?
186
+ critical 'Check all services was asked but no services was found' if services.none?
187
+ services.each do |service|
188
+ process_docker_logs service do |log_chunk|
189
+ problem_string = detect_problem(log_chunk)
190
+ break unless problem_string.nil?
191
+ end
192
+ problem << "\tError found inside service : '#{service}'\n\t\t#{problem_string}" unless problem_string.nil?
193
+ end
194
+ problem_string = problem.join("\n")
195
+ critical "service(s) logs indicate problems :\n#{problem_string}" unless problem.none?
196
+ services_string = services.join(', ')
197
+ ok "No errors detected from logs inside service(s) : \n#{services_string}"
198
+ end
199
+ end
@@ -0,0 +1,102 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-service
4
+ #
5
+ # DESCRIPTION:
6
+ # This is a simple check script for Sensu to check that a Docker service is
7
+ # running all of it's intended tasks. You can pass in either a service id or a service name.
8
+ #
9
+ # OUTPUT:
10
+ # plain text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ #
18
+ # USAGE:
19
+ # check-service.rb -H /var/run/docker.sock -N c92d402a5d14
20
+ # CheckDockerService OK: c92d402a5d14 is running on /var/run/docker.sock.
21
+ #
22
+ # check-service.rb -H https://127.0.0.1:2376 -N circle_burglar
23
+ # CheckDockerService CRITICAL: circle_burglar is not running on https://127.0.0.1:2376
24
+ #
25
+ # NOTES:
26
+ # => .Replicas == number of service's tasks -> OK
27
+ # => .Replicas != number of service's tasks -> CRITICAL
28
+ # => Not Found -> CRITICAL
29
+ # => Can't connect to Docker -> WARNING
30
+ # => Other exception -> WARNING
31
+ #
32
+ # LICENSE:
33
+ # Copyright 2014 Sonian, Inc. and contributors. <support@sensuapp.org>
34
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
35
+ # for details.
36
+ #
37
+
38
+ require 'sensu-plugin/check/cli'
39
+ require 'sensu-plugins-docker/client_helpers'
40
+
41
+ #
42
+ # Check Docker Service
43
+ #
44
+ class CheckDockerService < Sensu::Plugin::Check::CLI
45
+ option :docker_host,
46
+ short: '-H DOCKER_HOST',
47
+ long: '--docker-host DOCKER_HOST',
48
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path'
49
+
50
+ option :service,
51
+ short: '-N SERVICE',
52
+ long: '--service-name service',
53
+ required: true
54
+
55
+ option :tag,
56
+ short: '-t TAG',
57
+ long: '--tag TAG'
58
+
59
+ option :allowexited,
60
+ short: '-x',
61
+ long: '--allow-exited',
62
+ boolean: true,
63
+ description: 'Do not raise alert if service has exited without error'
64
+
65
+ def run
66
+ # Connect a client to the remote/local docker socket
67
+ @client = DockerApi.new(config[:docker_host])
68
+
69
+ # Call /services and get the service we want to check
70
+ path = "/services?filters=%7B%22name%22%3A%7B%22#{config[:service]}%22%3Atrue%7D%7D"
71
+ response = @client.call(path, false)
72
+ if response.code.to_i == 404
73
+ critical "service #{config[:service]} is not running on #{@client.uri} bb"
74
+ end
75
+
76
+ # Pass the number of replicas the service should be running
77
+ body = parse_json(response)
78
+ intended_replicas = body[0]['Spec']['Mode']['Replicated']['Replicas']
79
+
80
+ # Call /tasks to get the number of running replicas (this is how `docker service ls` works)
81
+ running_replicas = 0
82
+ path = "/tasks?filters=%7B%22name%22%3A%7B%22#{config[:service]}%22%3Atrue%7D%7D"
83
+ response = @client.call(path, false)
84
+ if response.code.to_i == 404
85
+ critical "service #{config[:service]} is not running on #{@client.uri}, tasks not found"
86
+ end
87
+
88
+ # Traverse the tasks and check if the state is running
89
+ tasks = parse_json(response)
90
+ tasks.each do |task|
91
+ if task['Status']['State'] == 'running'
92
+ running_replicas += 1
93
+ end
94
+ end
95
+
96
+ # If the number of running replicas is not what the service expects, return critical
97
+ if intended_replicas != running_replicas
98
+ critical "service #{config[:service]} is not running the intended number of replicas"
99
+ end
100
+ ok "#{config[:service]} is running correctly on #{@client.uri}."
101
+ end
102
+ end