sensu-plugins-docker 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5d47d0604805095860811b66cc120009fc1cb61b05927986499c6db4a6bb420
4
- data.tar.gz: 500444a84707e89a229aad4fbdfd3b0529df2a6b20e40d56e01aff9560bd87f3
3
+ metadata.gz: 00d6b43c8b66736073dd792abffe2d13f99034f0981795bf21663412ec64865e
4
+ data.tar.gz: 3f1fd2aedfcd7ac02eb06a8f5656b3527d670ef77fc61d192901b5d78770af37
5
5
  SHA512:
6
- metadata.gz: 8d9b0ae9d2e34e33a54b19a4f8685b4cc6cdaad21de250cd195f83344876185c0fea9c79beee05082962d1388c6447c3e1095eb140945bf93d6a6ea259b83ae6
7
- data.tar.gz: 96728604303b452f6d8955d56c3e537c9ab3260c0805c91b6605e77b9987282b66ecb55f2ff7fb9651b04f8dbe31dbf31f75b30e7e694321da084c4f8b64e766
6
+ metadata.gz: 58a4c4cf16098e2e57d0b28b38e6061b842d54df4deecca9c4e08f0bbca288541ac4faf095801b5f33627438aa56d2d4db01d99a7170e3f05cb8ec4a5225654f
7
+ data.tar.gz: 57fcb788326eee5cdb4a4df9aa731637f7407b8d51eacd901da12d1573e035c4f060884304bb00478a6ba48f35aae3610107c10429a43982fbed85bd3990e369
@@ -6,6 +6,36 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/)
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.0.0] - 2018-02-17
10
+ ### Breaking Changes
11
+ - Default docker host defined by DockerApi Class ( ENV[DOCKER_URL] => ENV[DOCKER_HOST] => /var/run/docker.sock )
12
+ - check-container-logs.rb: -N (--container-name) instead of -n for container name. Now a 'CRITICAL' is trigger if a container doesn't exist (previously, a 'OK' was trigger)
13
+ - check-docker-container.rb: -H (--docker-host) instead of -h (--host) for docker Host
14
+ - check-container.rb: -H (--docker-host) for docker Host instead of -h (--host) for docker host, -N (--container-name) instead of -c (--container) for container name
15
+ - metrics-docker-stats.rb: -N (--container-name) instead of -c (--container) for Container name
16
+
17
+ ### Added
18
+ - client_helpers.rb: Add a simple DockerApi class. Add parse_json method.
19
+ - metrics-docker-container.rb: Friendly names option added
20
+ - check-container-logs.rb: Add an option to check logs from stopped containers if all containers are checked. Add an option to don't check stderr logs. Add an option to don't check stdout logs. Add timestamp in logs output. Add the possibility to use -n multiple times to check multiple containers at once.
21
+
22
+ ### Changed
23
+ - metrics-docker-stats.rb: Make use of DockerApi class. Default docker_host defined by DockerApi class. Remove docker_api method.
24
+ - metrics-docker-info.rb: Make use of DockerApi class. Default docker_host defined by DockerApi class. Remove docker_api method.
25
+ - metrics-docker-container.rb: Make use of DockerApi class. Re-enable rubocop for container_metrics method.
26
+ - check-container.rb: Make use of DockerApi class.
27
+ - check-docker-container.rb: Make use of DockerApi class.
28
+ - check-container-logs.rb: Make use of DockerApi class. Check only logs generated with the 8 bits control to prevent to check logs generated in interactive mode. Check the newest logs rows first instead the oldest. Option -n is not required anymore, if -n option is not provided, the check will be applied to all running containers. Changed the messages displayed with ok and critical
29
+
30
+ ### Fixed
31
+ - metrics-docker-stats.rb: Remove trailing / in name value.
32
+
33
+ ### Removed
34
+ - Remove unnecessary `docker_api` dependency
35
+ - check-container-logs.rb: Logs generated in interactive mode are not checked anymore
36
+ - metrics-docker-stats.rb: option -p (--protocol) have been removed because new DockerApi don't use it
37
+ - metrics-docker-info.rb: option -p (--protocol) have been removed because new DockerApi don't use it
38
+
9
39
  ## [2.0.0] - 2017-11-06
10
40
  ### Fixed
11
41
  - metrics-docker-stats.rb:: Fix gsub on nil docker environment variable (@epierotto)
@@ -111,7 +141,8 @@ changes some options. Review your check commands before deploying this version.
111
141
  ### Added
112
142
  - initial release
113
143
 
114
- [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/2.0.0...HEAD
144
+ [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/3.0.0...HEAD
145
+ [3.0.0]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/2.0.0...3.0.0
115
146
  [2.0.0]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/1.5.0...2.0.0
116
147
  [1.5.0]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/1.4.0..1.5.0
117
148
  [1.4.0]: https://github.com/sensu-plugins/sensu-plugins-docker/compare/1.3.1...1.4.0
data/README.md CHANGED
@@ -19,6 +19,15 @@ This check supports docker versions >= 1.18. Check docker-engine API for more in
19
19
 
20
20
  ## Usage
21
21
 
22
+ ### Default docker host
23
+ By default, all the checks will try to use a default docker host if a specific docker host is not provided to the check on the command line (-H <docker_host> / --docker-host <docker_host>).
24
+
25
+ Those paramaters will be tried in this order as default docker host :
26
+
27
+ DOCKER_URL environnement variable
28
+ DOCKER_HOST environnement variable
29
+ /var/run/docker.sock file
30
+
22
31
  ## Installation
23
32
 
24
33
  [Installation and Setup](http://sensu-plugins.io/docs/installation_instructions.html)
@@ -17,10 +17,21 @@
17
17
  # gem: net_http_unix
18
18
  #
19
19
  # USAGE:
20
- # check-container-logs.rb -H /tmp/docker.sock -n logspout -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
20
+ # # Check only one container
21
+ # check-container-logs.rb -H /tmp/docker.sock -N logspout -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
21
22
  # => 1 container running = OK
22
23
  # => 4 container running = CRITICAL
23
24
  #
25
+ # # Check multiple containers
26
+ # check-container-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 container running = OK
28
+ # => 4 container running = CRITICAL
29
+ #
30
+ # # Check all containers
31
+ # check-container-logs.rb -H /tmp/docker.sock -r 'problem sending' -r 'i/o timeout' -i 'Remark:' -i 'The configuration is'
32
+ # => 1 containers running = OK
33
+ # => 4 containers running = CRITICAL
34
+ #
24
35
  # NOTES:
25
36
  # The API parameter required to use the limited lookback (-t) was introduced
26
37
  # the Docker server API version 1.19. This check may still work on older API
@@ -37,36 +48,33 @@ require 'sensu-plugins-docker/client_helpers'
37
48
 
38
49
  class ContainerLogChecker < Sensu::Plugin::Check::CLI
39
50
  option :docker_host,
40
- description: 'Docker socket to connect. TCP: "host:port" or Unix: "/path/to/docker.sock" (default: "127.0.0.1:2375")',
51
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
41
52
  short: '-H DOCKER_HOST',
42
- long: '--docker-host DOCKER_HOST',
43
- default: '127.0.0.1:2375'
53
+ long: '--docker-host DOCKER_HOST'
44
54
 
45
55
  option :container,
46
- description: 'name of container',
47
- short: '-n CONTAINER',
56
+ description: 'name of container; can be used multiple times. /!\ All running containers will be check if this options is not provided',
57
+ short: '-N CONTAINER',
48
58
  long: '--container-name CONTAINER',
49
- required: true
59
+ default: [],
60
+ proc: proc { |flag| (@options[:container][:accumulated] ||= []).push(flag) }
50
61
 
51
62
  option :red_flags,
52
- description: 'substring whose presence (case-insensitive by default) in a log line indicates an error; can be used multiple t
53
- imes',
54
- short: '-r "error occurred" -r "problem encountered" -r "error status"',
55
- long: '--red-flag "error occurred" --red-flag "problem encountered" --red-flag "error status"',
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',
56
66
  default: [],
57
67
  proc: proc { |flag| (@options[:red_flags][:accumulated] ||= []).push(flag) }
58
68
 
59
69
  option :ignore_list,
60
- description: 'substring whose presence (case-insensitive by default) in a log line indicates the line should be ignored; can
61
- be used multiple times',
62
- short: '-i "configuration:" -i "# Remark:"',
63
- long: '--ignore-lines-with "configuration:" --ignore-lines-with "# remark:"',
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',
64
73
  default: [],
65
74
  proc: proc { |flag| (@options[:ignore_list][:accumulated] ||= []).push(flag) }
66
75
 
67
76
  option :case_sensitive,
68
- description: 'indicates all red_flag and ignore_list substring matching should be case-sensitive instead of the default case-
69
- insensitive',
77
+ description: 'indicates all red_flag and ignore_list substring matching should be case-sensitive instead of the default case-insensitive',
70
78
  short: '-c',
71
79
  long: '--case-sensitive',
72
80
  boolean: true
@@ -83,33 +91,60 @@ insensitive',
83
91
  long: '--seconds-ago SECONDS',
84
92
  required: false
85
93
 
94
+ option :check_all,
95
+ description: 'If all containers are checked (no container name provided with -n) , check offline containers 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
+
86
117
  def calculate_timestamp(seconds_ago = nil)
87
118
  seconds_ago = yield if block_given?
88
119
  (Time.now - seconds_ago).to_i
89
120
  end
90
121
 
91
122
  def process_docker_logs(container_name)
92
- client = create_docker_client
93
- path = "/containers/#{container_name}/logs?stdout=true&stderr=true"
123
+ path = "/containers/#{container_name}/logs?stdout=#{config[:disable_stdout]}&stderr=#{config[:disable_stderr]}&timestamps=true"
94
124
  if config.key? :hours_ago
95
125
  timestamp = calculate_timestamp { config[:hours_ago].to_i * 3600 }
96
126
  elsif config.key? :seconds_ago
97
127
  timestamp = calculate_timestamp config[:seconds_ago].to_i
98
128
  end
99
129
  path = "#{path}&since=#{timestamp}"
100
- req = Net::HTTP::Get.new path
101
-
102
- client.request req do |response|
103
- response.read_body do |chunk|
104
- yield remove_headers chunk
105
- end
130
+ response = @client.call(path, false)
131
+ if response.code.to_i == 404
132
+ critical "Container '#{container_name}' not found on #{@client.uri}"
106
133
  end
134
+ yield remove_headers response.read_body
107
135
  end
108
136
 
109
137
  def remove_headers(raw_logs)
110
138
  lines = raw_logs.split("\n")
111
- lines.map! { |line| line.byteslice(8, line.bytesize) }
112
- lines.join("\n")
139
+ lines.map! do |line|
140
+ # Check only logs generated with the 8 bits control
141
+ if !line.nil? && /^(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")
113
148
  end
114
149
 
115
150
  def includes_any?(str, array_of_substrings)
@@ -135,11 +170,30 @@ insensitive',
135
170
  end
136
171
 
137
172
  def run
138
- container = config[:container]
139
- process_docker_logs(container) do |log_chunk|
140
- problem = detect_problem log_chunk
141
- critical "#{container} container logs indicate problem: '#{problem}'." unless problem.nil?
173
+ @client = DockerApi.new(config[:docker_host])
174
+ problem = []
175
+ problem_string = nil
176
+ path = "/containers/json?all=#{config[:check_all]}"
177
+ containers = config[:container]
178
+ if config[:container].none?
179
+ warn_msg = %(
180
+ Collecting logs from all containers 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
+ containers = @client.parse(path).map { |p| p['Names'][0].delete('/') } if containers.none?
186
+ critical 'Check all containers was asked but no containers was found' if containers.none?
187
+ containers.each do |container|
188
+ process_docker_logs container do |log_chunk|
189
+ problem_string = detect_problem(log_chunk)
190
+ break unless problem_string.nil?
191
+ end
192
+ problem << "\tError found inside container : '#{container}'\n\t\t#{problem_string}" unless problem_string.nil?
142
193
  end
143
- ok "No errors detected from #{container} container logs."
194
+ problem_string = problem.join("\n")
195
+ critical "Container(s) logs indicate problems :\n#{problem_string}" unless problem.none?
196
+ containers_string = containers.join(', ')
197
+ ok "No errors detected from logs inside container(s) : \n#{containers_string}"
144
198
  end
145
199
  end
@@ -16,11 +16,11 @@
16
16
  # gem: sensu-plugin
17
17
  #
18
18
  # USAGE:
19
- # check-container.rb -h /var/run/docker.sock -c c92d402a5d14
19
+ # check-container.rb -H /var/run/docker.sock -N c92d402a5d14
20
20
  # CheckDockerContainer OK: c92d402a5d14 is running on /var/run/docker.sock.
21
21
  #
22
- # check-container.rb -h /var/run/docker.sock -c circle_burglar
23
- # CheckDockerContainer CRITICAL: circle_burglar is not running on /var/run/docker.sock
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
24
  #
25
25
  # NOTES:
26
26
  # => State.running == true -> OK
@@ -37,52 +37,45 @@
37
37
 
38
38
  require 'sensu-plugin/check/cli'
39
39
  require 'sensu-plugins-docker/client_helpers'
40
- require 'json'
41
40
 
42
41
  #
43
42
  # Check Docker Container
44
43
  #
45
44
  class CheckDockerContainer < Sensu::Plugin::Check::CLI
46
45
  option :docker_host,
47
- short: '-h DOCKER_HOST',
48
- long: '--host DOCKER_HOST',
49
- description: 'Docker socket to connect. TCP: "host:port" or Unix: "/path/to/docker.sock" (default: "127.0.0.1:2375")',
50
- default: '127.0.0.1:2375'
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
+
51
50
  option :container,
52
- short: '-c CONTAINER',
53
- long: '--container CONTAINER',
51
+ short: '-N CONTAINER',
52
+ long: '--container-name CONTAINER',
54
53
  required: true
54
+
55
55
  option :tag,
56
56
  short: '-t TAG',
57
57
  long: '--tag TAG'
58
58
 
59
59
  def run
60
- client = create_docker_client
60
+ @client = DockerApi.new(config[:docker_host])
61
61
  path = "/containers/#{config[:container]}/json"
62
- req = Net::HTTP::Get.new path
63
- begin
64
- response = client.request(req)
65
- if response.code.to_i == 404
66
- critical "#{config[:container]} is not running on #{config[:docker_host]}"
67
- end
68
- body = JSON.parse(response.body)
69
- container_running = body['State']['Running']
70
- if container_running
71
- if config[:tag]
72
- image = body['Config']['Image']
73
- match = image.match(/^(?:([^\/]+)\/)?(?:([^\/]+)\/)?([^@:\/]+)(?:[@:](.+))?$/)
74
- unless match && match[4] == config[:tag]
75
- critical "#{config[:container]}'s tag is '#{match[4]}', excepting '#{config[:tag]}'"
76
- end
62
+ response = @client.call(path, false)
63
+ if response.code.to_i == 404
64
+ critical "Container #{config[:container]} is not running on #{@client.uri}"
65
+ end
66
+ body = parse_json(response)
67
+ container_running = body['State']['Running']
68
+ if container_running
69
+ if config[:tag]
70
+ image = body['Config']['Image']
71
+ match = image.match(/^(?:([^\/]+)\/)?(?:([^\/]+)\/)?([^@:\/]+)(?:[@:](.+))?$/)
72
+ unless match && match[4] == config[:tag]
73
+ critical "#{config[:container]}'s tag is '#{match[4]}', especting '#{config[:tag]}'"
77
74
  end
78
- ok "#{config[:container]} is running on #{config[:docker_host]}."
79
- else
80
- critical "#{config[:container]} is #{body['State']['Status']} on #{config[:docker_host]}."
81
75
  end
82
- rescue JSON::ParserError => e
83
- critical "JSON Error: #{e.inspect}"
84
- rescue => e
85
- warning "Error: #{e.inspect}"
76
+ ok "#{config[:container]} is running on #{@client.uri}."
77
+ else
78
+ critical "#{config[:container]} is #{body['State']['Status']} on #{@client.uri}."
86
79
  end
87
80
  end
88
81
  end
@@ -20,6 +20,14 @@
20
20
  # => 1 container running = OK.
21
21
  # => 4 container running = CRITICAL
22
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
+ #
23
31
  # NOTES:
24
32
  #
25
33
  # LICENSE:
@@ -30,16 +38,15 @@
30
38
 
31
39
  require 'sensu-plugin/check/cli'
32
40
  require 'sensu-plugins-docker/client_helpers'
33
- require 'json'
41
+
34
42
  #
35
43
  # Check Docker Containers
36
44
  #
37
45
  class CheckDockerContainers < Sensu::Plugin::Check::CLI
38
46
  option :docker_host,
39
- short: '-h docker_host',
40
- long: '--host DOCKER_HOST',
41
- description: 'Docker socket to connect. TCP: "host:port" or Unix: "/path/to/docker.sock" (default: "127.0.0.1:2375")',
42
- default: '127.0.0.1:2375'
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'
43
50
 
44
51
  option :warn_over,
45
52
  short: '-W N',
@@ -94,17 +101,8 @@ class CheckDockerContainers < Sensu::Plugin::Check::CLI
94
101
  end
95
102
 
96
103
  def run
97
- client = create_docker_client
98
- path = '/containers/json'
99
- req = Net::HTTP::Get.new path
100
- begin
101
- response = client.request(req)
102
- containers = JSON.parse(response.body)
103
- evaluate_count containers.size
104
- rescue JSON::ParserError => e
105
- critical "JSON Error: #{e.inspect}"
106
- rescue => e
107
- warning "Error: #{e.inspect}"
108
- end
104
+ @client = DockerApi.new(config[:docker_host])
105
+ containers = @client.parse('/containers/json')
106
+ evaluate_count containers.size
109
107
  end
110
108
  end
@@ -14,7 +14,22 @@
14
14
  # gem: sensu-plugin
15
15
  #
16
16
  # USAGE:
17
- # #YELLOW
17
+ #
18
+ # metrics-docker-container.rb -H /var/run/docker.sock -n
19
+ # docker.host.sensu.sh.rss 1 1508825823
20
+ # docker.host.sensu.sh.vsize 1572864 1508825823
21
+ # docker.host.sensu.sh.nswap 0 1508825823
22
+ # docker.host.sensu.sh.pctmem 0.0 1508825823
23
+ # docker.host.sensu.sh.fd 0 1508825823
24
+ # docker.host.sensu.sh.cpu 0 1508825823
25
+ #
26
+ # metrics-docker-container.rb -H https://127.0.0.1:2376
27
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.rss 183 1508825793
28
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.vsize 4616192 1508825793
29
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.nswap 0 1508825793
30
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.pctmem 0.01 1508825793
31
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.fd 0 1508825793
32
+ # docker.host.ff5240e079488d248c021f8da5d13074a6a9db72ffaf1129eded445f4e16cf50.sh.cpu 0 1508825793
18
33
  #
19
34
  # NOTES:
20
35
  #
@@ -25,7 +40,7 @@
25
40
  #
26
41
 
27
42
  require 'sensu-plugin/metric/cli'
28
- require 'socket'
43
+ require 'sensu-plugins-docker/client_helpers'
29
44
  require 'pathname'
30
45
  require 'sys/proctable'
31
46
 
@@ -46,23 +61,30 @@ class DockerContainerMetrics < Sensu::Plugin::Metric::CLI::Graphite
46
61
  default: '/sys/fs/cgroup'
47
62
 
48
63
  option :docker_host,
49
- description: 'Docker host. TCP: "tcp://host:port" or Unix: "unix:///path/to/docker.sock" (default: "tcp://127.0.1.1:2376")',
64
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
50
65
  short: '-H DOCKER_HOST',
51
- long: '--docker-host DOCKER_HOST',
52
- default: 'tcp://127.0.1.1:2376'
66
+ long: '--docker-host DOCKER_HOST'
53
67
 
54
68
  option :cgroup_template,
55
69
  description: 'cgroup_template',
56
- short: '-T <template string>',
57
- long: '--cgroup-template template_string',
70
+ short: '-T TPL_STRING',
71
+ long: '--cgroup-template TPL_STRING',
58
72
  default: 'cpu/docker/%{container}/cgroup.procs'
59
73
 
74
+ option :friendly_names,
75
+ description: 'use friendly name if available',
76
+ short: '-n',
77
+ long: '--names',
78
+ boolean: true,
79
+ default: false
80
+
60
81
  def run
82
+ @client = DockerApi.new(config[:docker_host])
61
83
  container_metrics
62
84
  ok
63
85
  end
64
86
 
65
- def container_metrics #rubocop:disable all
87
+ def container_metrics
66
88
  cgroup = "#{config[:cgroup_path]}/#{config[:cgroup_template]}"
67
89
 
68
90
  timestamp = Time.now.to_i
@@ -72,18 +94,24 @@ class DockerContainerMetrics < Sensu::Plugin::Metric::CLI::Graphite
72
94
 
73
95
  fields = [:rss, :vsize, :nswap, :pctmem]
74
96
 
75
- ENV['DOCKER_HOST'] = config[:docker_host]
76
- containers = `docker ps --quiet --no-trunc`.split("\n")
97
+ path = '/containers/json'
98
+ containers = @client.parse(path)
77
99
 
78
100
  containers.each do |container|
79
- path = Pathname(format(cgroup, container: container))
101
+ path = Pathname(format(cgroup, container: container['Id']))
80
102
  pids = path.readlines.map(&:to_i)
81
103
 
104
+ container_name = if config[:friendly_names]
105
+ container['Names'][0].delete('/')
106
+ else
107
+ container['Id']
108
+ end
109
+
82
110
  processes = ps.values_at(*pids).flatten.compact.group_by(&:comm)
83
111
  processes2 = ps2.values_at(*pids).flatten.compact.group_by(&:comm)
84
112
 
85
113
  processes.each do |comm, process|
86
- prefix = "#{config[:scheme]}.#{container}.#{comm}"
114
+ prefix = "#{config[:scheme]}.#{container_name}.#{comm}"
87
115
  fields.each do |field|
88
116
  output "#{prefix}.#{field}", process.map(&field).reduce(:+), timestamp
89
117
  end
@@ -21,10 +21,10 @@
21
21
  #
22
22
  # USAGE:
23
23
  # Gather stats using unix socket:
24
- # metrics-docker-info.rb -p unix -H /var/run/docker.sock
24
+ # metrics-docker-info.rb -H /var/run/docker.sock
25
25
  #
26
- # Gather stats from localhost using TCP:
27
- # metrics-docker-info.rb -p http -H localhost:2375
26
+ # Gather stats from localhost using HTTP:
27
+ # metrics-docker-info.rb -H localhost:2375
28
28
  #
29
29
  # See metrics-docker-info.rb --help for full usage flags
30
30
  #
@@ -37,9 +37,7 @@
37
37
  #
38
38
 
39
39
  require 'sensu-plugin/metric/cli'
40
- require 'socket'
41
- require 'net_http_unix'
42
- require 'json'
40
+ require 'sensu-plugins-docker/client_helpers'
43
41
 
44
42
  class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
45
43
  option :scheme,
@@ -49,43 +47,19 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
49
47
  default: "#{Socket.gethostname}.docker"
50
48
 
51
49
  option :docker_host,
52
- description: 'Docker socket to connect. TCP: "host:port" or Unix: "/path/to/docker.sock" (default: "127.0.0.1:2375")',
50
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
53
51
  short: '-H DOCKER_HOST',
54
- long: '--docker-host DOCKER_HOST',
55
- default: '/var/run/docker.sock'
56
-
57
- option :docker_protocol,
58
- description: 'http or unix',
59
- short: '-p PROTOCOL',
60
- long: '--protocol PROTOCOL',
61
- default: 'unix'
52
+ long: '--docker-host DOCKER_HOST'
62
53
 
63
54
  def run
64
55
  @timestamp = Time.now.to_i
65
- path = 'info'
66
- infolist = docker_api(path)
56
+ @client = DockerApi.new(config[:docker_host])
57
+ path = '/info'
58
+ infolist = @client.parse(path)
67
59
  filtered_list = infolist.select { |key, _value| key.match(/NCPU|NFd|Containers|Images|NGoroutines|NEventsListener|MemTotal/) }
68
60
  filtered_list.each do |key, value|
69
61
  output "#{config[:scheme]}.#{key}", value, @timestamp
70
62
  end
71
63
  ok
72
64
  end
73
-
74
- def docker_api(path)
75
- if config[:docker_protocol] == 'unix'
76
- session = NetX::HTTPUnix.new("unix://#{config[:docker_host]}")
77
- request = Net::HTTP::Get.new "/#{path}"
78
- else
79
- uri = URI("#{config[:docker_protocol]}://#{config[:docker_host]}/#{path}")
80
- session = Net::HTTP.new(uri.host, uri.port)
81
- request = Net::HTTP::Get.new uri.request_uri
82
- end
83
-
84
- session.start do |http|
85
- http.request request do |response|
86
- response.value
87
- return JSON.parse(response.read_body)
88
- end
89
- end
90
- end
91
65
  end
@@ -19,13 +19,13 @@
19
19
  #
20
20
  # USAGE:
21
21
  # Gather stats from all containers on a host using socket:
22
- # metrics-docker-stats.rb -p unix -H /var/run/docker.sock
22
+ # metrics-docker-stats.rb -H /var/run/docker.sock
23
23
  #
24
- # Gather stats from all containers on a host using TCP:
25
- # metrics-docker-stats.rb -p http -H localhost:2375
24
+ # Gather stats from all containers on a host using HTTP:
25
+ # metrics-docker-stats.rb -H localhost:2375
26
26
  #
27
27
  # Gather stats from a specific container using socket:
28
- # metrics-docker-stats.rb -p unix -H /var/run/docker.sock -c 5bf1b82382eb
28
+ # metrics-docker-stats.rb -H /var/run/docker.sock -N 5bf1b82382eb
29
29
  #
30
30
  # See metrics-docker-stats.rb --help for full usage flags
31
31
  #
@@ -38,9 +38,7 @@
38
38
  #
39
39
 
40
40
  require 'sensu-plugin/metric/cli'
41
- require 'socket'
42
- require 'net_http_unix'
43
- require 'json'
41
+ require 'sensu-plugins-docker/client_helpers'
44
42
 
45
43
  class Hash
46
44
  def self.to_dotted_hash(hash, recursive_key = '')
@@ -64,21 +62,14 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
64
62
 
65
63
  option :container,
66
64
  description: 'Name of container to collect metrics for',
67
- short: '-c CONTAINER',
68
- long: '--container CONTAINER',
65
+ short: '-N CONTAINER',
66
+ long: '--container-name CONTAINER',
69
67
  default: ''
70
68
 
71
69
  option :docker_host,
72
- description: 'Docker socket to connect. TCP: "host:port" or Unix: "/path/to/docker.sock" (default: "127.0.0.1:2375")',
70
+ description: 'Docker API URI. https://host, https://host:port, http://host, http://host:port, host:port, unix:///path',
73
71
  short: '-H DOCKER_HOST',
74
- long: '--docker-host DOCKER_HOST',
75
- default: '127.0.0.1:2375'
76
-
77
- option :docker_protocol,
78
- description: 'http or unix',
79
- short: '-p PROTOCOL',
80
- long: '--protocol PROTOCOL',
81
- default: 'http'
72
+ long: '--docker-host DOCKER_HOST'
82
73
 
83
74
  option :friendly_names,
84
75
  description: 'use friendly name if available',
@@ -88,8 +79,8 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
88
79
  default: false
89
80
 
90
81
  option :name_parts,
91
- description: 'Partial names by spliting and returning at index(es). \
92
- eg. -m 3,4 my-docker-container-process_name-b2ffdab8f1aceae85300 for process_name.b2ffdab8f1aceae85300',
82
+ description: 'Partial names by spliting and returning at index(es).
83
+ eg. -m 3,4 my-docker-container-process_name-b2ffdab8f1aceae85300 for process_name.b2ffdab8f1aceae85300',
93
84
  short: '-m index',
94
85
  long: '--match index'
95
86
 
@@ -101,8 +92,8 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
101
92
 
102
93
  option :environment_tags,
103
94
  description: 'Name of environment variables on each container to be appended to metric name, separated by commas',
104
- short: '-e ENVIRONMENT_VARIABLES',
105
- long: '--environment-tags ENVIRONMENT_VARIABLES'
95
+ short: '-e ENV_VARS',
96
+ long: '--environment-tags ENV_VARS'
106
97
 
107
98
  option :ioinfo,
108
99
  description: 'enable IO Docker metrics',
@@ -120,6 +111,7 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
120
111
 
121
112
  def run
122
113
  @timestamp = Time.now.to_i
114
+ @client = DockerApi.new(config[:docker_host])
123
115
 
124
116
  list = if config[:container] != ''
125
117
  [config[:container]]
@@ -150,6 +142,7 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
150
142
  dotted_stats.each do |key, value|
151
143
  next if key == 'read' # unecessary timestamp
152
144
  next if value.is_a?(Array)
145
+ value.delete!('/') if key == 'name'
153
146
  output "#{config[:scheme]}.#{container}.#{key}", value, @timestamp
154
147
  end
155
148
  if config[:ioinfo]
@@ -160,30 +153,12 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
160
153
  output "#{config[:scheme]}.#{container}.cpu_stats.usage_percent", calculate_cpu_percent(stats), @timestamp if config[:cpupercent]
161
154
  end
162
155
 
163
- def docker_api(path)
164
- if config[:docker_protocol] == 'unix'
165
- session = NetX::HTTPUnix.new("unix://#{config[:docker_host]}")
166
- request = Net::HTTP::Get.new "/#{path}"
167
- else
168
- uri = URI("#{config[:docker_protocol]}://#{config[:docker_host]}/#{path}")
169
- session = Net::HTTP.new(uri.host, uri.port)
170
- request = Net::HTTP::Get.new uri.request_uri
171
- end
172
-
173
- session.start do |http|
174
- http.request request do |response|
175
- response.value
176
- return JSON.parse(response.read_body)
177
- end
178
- end
179
- end
180
-
181
156
  def list_containers
182
157
  list = []
183
- path = 'containers/json'
184
- @containers = docker_api(path)
158
+ path = '/containers/json'
159
+ containers = @client.parse(path)
185
160
 
186
- @containers.each do |container|
161
+ containers.each do |container|
187
162
  list << if config[:friendly_names]
188
163
  container['Names'][0].delete('/')
189
164
  elsif config[:name_parts]
@@ -196,17 +171,25 @@ class DockerStatsMetrics < Sensu::Plugin::Metric::CLI::Graphite
196
171
  end
197
172
 
198
173
  def container_stats(container)
199
- path = "containers/#{container}/stats?stream=0"
200
- @stats = docker_api(path)
174
+ path = "/containers/#{container}/stats?stream=0"
175
+ response = @client.call(path)
176
+ if response.code.to_i == 404
177
+ critical "#{config[:container]} is not running on #{@client.uri}"
178
+ end
179
+ parse_json(response)
201
180
  end
202
181
 
203
182
  def container_tags(container)
204
183
  tags = ''
205
- path = "containers/#{container}/json"
206
- @inspect = docker_api(path)
184
+ path = "/containers/#{container}/json"
185
+ response = @client.call(path)
186
+ if response.code.to_i == 404
187
+ critical "#{config[:container]} is not running on #{@client.uri}"
188
+ end
189
+ inspect = parse_json(response)
207
190
  tag_list = config[:environment_tags].split(',')
208
191
  tag_list.each do |value|
209
- tags << @inspect['Config']['Env'].select { |tag| tag.to_s.match(/#{value}=/) }.first.to_s.gsub(/#{value}=/, '') + '.'
192
+ tags << inspect['Config']['Env'].select { |tag| tag.to_s.match(/#{value}=/) }.first.to_s.gsub(/#{value}=/, '') + '.'
210
193
  end
211
194
  tags
212
195
  end
@@ -1,18 +1,60 @@
1
1
  require 'net_http_unix'
2
+ require 'json'
2
3
 
3
- def create_docker_client
4
- client = nil
5
- if config[:docker_host][0] == '/'
6
- host = 'unix://' + config[:docker_host]
7
- client = NetX::HTTPUnix.new(host)
8
- else
9
- split_host = config[:docker_host].split ':'
10
- client = if split_host.length == 2
11
- NetX::HTTPUnix.new(split_host[0], split_host[1])
12
- else
13
- NetX::HTTPUnix.new(config[:docker_host], 2375)
14
- end
4
+ class DockerApi
5
+ def initialize(uri = nil)
6
+ @client = nil
7
+ @docker_uri = uri || ENV['DOCKER_URL'] || ENV['DOCKER_HOST'] || '/var/run/docker.sock'
8
+ if @docker_uri.sub!(%r{^(unix://)?/}, '')
9
+ @docker_uri = 'unix:///' + @docker_uri
10
+ @client = NetX::HTTPUnix.new(@docker_uri)
11
+ else
12
+ protocol = %r{^(https?|tcp)://}.match(@docker_uri) || 'http://'
13
+ @docker_uri.sub!(protocol.to_s, '')
14
+ split_host = @docker_uri.split ':'
15
+ @client = if split_host.length == 2
16
+ NetX::HTTPUnix.new("#{protocol}#{split_host[0]}", split_host[1])
17
+ else
18
+ NetX::HTTPUnix.new("#{protocol}#{@docker_uri}", 2375)
19
+ end
20
+ end
21
+ @client.start
15
22
  end
16
23
 
17
- client
24
+ def uri
25
+ @docker_uri
26
+ end
27
+
28
+ def call(path, halt = true, limit = 10)
29
+ raise ArgumentError, "HTTP redirect too deep. Last url called : #{path}" if limit.zero?
30
+ if %r{^unix:///} =~ @docker_uri
31
+ request = Net::HTTP::Get.new path.to_s
32
+ else
33
+ uri = URI("#{@docker_uri}#{path}")
34
+ request = Net::HTTP::Get.new uri.request_uri
35
+ end
36
+ response = @client.request(request)
37
+ case response
38
+ when Net::HTTPSuccess then response
39
+ when Net::HTTPRedirection then call(response['location'], true, limit - 1)
40
+ else
41
+ return response.error! unless halt == false
42
+ return response
43
+ end
44
+ end
45
+
46
+ def parse(path, halt = true, limit = 10)
47
+ parsed = parse_json(call(path, halt, limit))
48
+ parsed
49
+ end
50
+ end
51
+
52
+ def parse_json(response)
53
+ parsed = nil
54
+ begin
55
+ parsed = JSON.parse(response.read_body)
56
+ rescue JSON::ParserError => e
57
+ raise "JSON Error: #{e.inspect}"
58
+ end
59
+ parsed
18
60
  end
@@ -1,6 +1,6 @@
1
1
  module SensuPluginsDocker
2
2
  module Version
3
- MAJOR = 2
3
+ MAJOR = 3
4
4
  MINOR = 0
5
5
  PATCH = 0
6
6
 
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu-plugins-docker
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sensu-Plugins and contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-07 00:00:00.000000000 Z
11
+ date: 2018-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: docker-api
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - '='
18
- - !ruby/object:Gem::Version
19
- version: 1.21.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - '='
25
- - !ruby/object:Gem::Version
26
- version: 1.21.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: sensu-plugin
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -199,12 +185,12 @@ description: |-
199
185
  metrics via `docker ps`
200
186
  email: "<sensu-users@googlegroups.com>"
201
187
  executables:
202
- - metrics-docker-container.rb
203
188
  - metrics-docker-info.rb
204
- - check-container.rb
189
+ - metrics-docker-container.rb
190
+ - check-docker-container.rb
205
191
  - check-container-logs.rb
206
192
  - metrics-docker-stats.rb
207
- - check-docker-container.rb
193
+ - check-container.rb
208
194
  extensions: []
209
195
  extra_rdoc_files: []
210
196
  files:
@@ -246,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
246
232
  version: '0'
247
233
  requirements: []
248
234
  rubyforge_project:
249
- rubygems_version: 2.7.1
235
+ rubygems_version: 2.7.6
250
236
  signing_key:
251
237
  specification_version: 4
252
238
  summary: Sensu plugins for docker