portertech-sensu 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +961 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/README.md +65 -0
  5. data/exe/sensu-api +10 -0
  6. data/exe/sensu-client +10 -0
  7. data/exe/sensu-install +195 -0
  8. data/exe/sensu-server +10 -0
  9. data/lib/sensu/api/http_handler.rb +434 -0
  10. data/lib/sensu/api/process.rb +79 -0
  11. data/lib/sensu/api/routes/aggregates.rb +196 -0
  12. data/lib/sensu/api/routes/checks.rb +44 -0
  13. data/lib/sensu/api/routes/clients.rb +171 -0
  14. data/lib/sensu/api/routes/events.rb +86 -0
  15. data/lib/sensu/api/routes/health.rb +45 -0
  16. data/lib/sensu/api/routes/info.rb +37 -0
  17. data/lib/sensu/api/routes/request.rb +44 -0
  18. data/lib/sensu/api/routes/resolve.rb +32 -0
  19. data/lib/sensu/api/routes/results.rb +153 -0
  20. data/lib/sensu/api/routes/settings.rb +23 -0
  21. data/lib/sensu/api/routes/silenced.rb +182 -0
  22. data/lib/sensu/api/routes/stashes.rb +107 -0
  23. data/lib/sensu/api/routes.rb +88 -0
  24. data/lib/sensu/api/utilities/filter_response_content.rb +44 -0
  25. data/lib/sensu/api/utilities/publish_check_request.rb +107 -0
  26. data/lib/sensu/api/utilities/publish_check_result.rb +39 -0
  27. data/lib/sensu/api/utilities/resolve_event.rb +29 -0
  28. data/lib/sensu/api/utilities/servers_info.rb +43 -0
  29. data/lib/sensu/api/utilities/transport_info.rb +43 -0
  30. data/lib/sensu/api/validators/check.rb +55 -0
  31. data/lib/sensu/api/validators/client.rb +35 -0
  32. data/lib/sensu/api/validators/invalid.rb +8 -0
  33. data/lib/sensu/cli.rb +69 -0
  34. data/lib/sensu/client/http_socket.rb +217 -0
  35. data/lib/sensu/client/process.rb +655 -0
  36. data/lib/sensu/client/socket.rb +207 -0
  37. data/lib/sensu/client/utils.rb +53 -0
  38. data/lib/sensu/client/validators/check.rb +53 -0
  39. data/lib/sensu/constants.rb +17 -0
  40. data/lib/sensu/daemon.rb +396 -0
  41. data/lib/sensu/sandbox.rb +19 -0
  42. data/lib/sensu/server/filter.rb +227 -0
  43. data/lib/sensu/server/handle.rb +201 -0
  44. data/lib/sensu/server/mutate.rb +92 -0
  45. data/lib/sensu/server/process.rb +1646 -0
  46. data/lib/sensu/server/socket.rb +54 -0
  47. data/lib/sensu/server/tessen.rb +170 -0
  48. data/lib/sensu/utilities.rb +398 -0
  49. data/lib/sensu.rb +3 -0
  50. data/sensu.gemspec +36 -0
  51. metadata +322 -0
@@ -0,0 +1,55 @@
1
+ require "sensu/api/validators/invalid"
2
+ require "sensu/settings/rules"
3
+ require "sensu/settings/validators/check"
4
+
5
+ module Sensu
6
+ module API
7
+ module Validators
8
+ class Check
9
+ # Include Sensu Settings rules and check validator.
10
+ include Sensu::Settings::Rules
11
+ include Sensu::Settings::Validators::Check
12
+
13
+ # Validate a check result, selectively using check definition
14
+ # validation methods.
15
+ #
16
+ # @param check [Hash]
17
+ def validate_check_result(check)
18
+ must_be_a_string(check[:output]) ||
19
+ invalid(check, "check output must be a string")
20
+ must_be_an_integer(check[:status]) ||
21
+ invalid(check, "check status must be an integer")
22
+ must_be_an_integer(check[:executed]) ||
23
+ invalid(check, "check executed timestamp must be an integer")
24
+ validate_check_name(check)
25
+ validate_check_handling(check)
26
+ validate_check_aggregate(check)
27
+ validate_check_flap_detection(check)
28
+ validate_check_truncate_output(check)
29
+ validate_check_source(check) if check[:source]
30
+ validate_check_ttl(check) if check[:ttl]
31
+ end
32
+
33
+ # Determine if a check definition is valid.
34
+ #
35
+ # @param check [Hash]
36
+ # @return [TrueClass, FalseClass]
37
+ def valid?(check)
38
+ validate_check_result(check)
39
+ true
40
+ rescue Invalid
41
+ false
42
+ end
43
+
44
+ private
45
+
46
+ # This method is called when validation methods encounter an
47
+ # invalid definition object. This method raises an exception
48
+ # to be caught by `valid?()`.
49
+ def invalid(*arguments)
50
+ raise Invalid
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ require "sensu/api/validators/invalid"
2
+ require "sensu/settings/rules"
3
+ require "sensu/settings/validators/client"
4
+
5
+ module Sensu
6
+ module API
7
+ module Validators
8
+ class Client
9
+ # Include Sensu Settings rules and client validator.
10
+ include Sensu::Settings::Rules
11
+ include Sensu::Settings::Validators::Client
12
+
13
+ # Determine if a client definition is valid.
14
+ #
15
+ # @param client [Hash]
16
+ # @return [TrueClass, FalseClass]
17
+ def valid?(client)
18
+ validate_client(client)
19
+ true
20
+ rescue Invalid
21
+ false
22
+ end
23
+
24
+ private
25
+
26
+ # This method is called when `validate_client()` encounters an
27
+ # invalid definition object. This method raises an exception
28
+ # to be caught by `valid?()`.
29
+ def invalid(*arguments)
30
+ raise Invalid
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ module Sensu
2
+ module API
3
+ module Validators
4
+ # The error class for validation.
5
+ class Invalid < RuntimeError; end
6
+ end
7
+ end
8
+ end
data/lib/sensu/cli.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "optparse"
2
+ require "sensu/logger/constants"
3
+
4
+ module Sensu
5
+ class CLI
6
+ # Parse CLI arguments using Ruby stdlib `optparse`. This method
7
+ # provides Sensu with process options (eg. log file), and can
8
+ # provide users with information, such as the Sensu version.
9
+ #
10
+ # @param arguments [Array] to parse.
11
+ # @return [Hash] options
12
+ def self.read(arguments=ARGV)
13
+ options = {}
14
+ if File.exist?("/etc/sensu/config.json")
15
+ options[:config_file] = "/etc/sensu/config.json"
16
+ end
17
+ if Dir.exist?("/etc/sensu/conf.d")
18
+ options[:config_dirs] = ["/etc/sensu/conf.d"]
19
+ end
20
+ optparse = OptionParser.new do |opts|
21
+ opts.on("-h", "--help", "Display this message") do
22
+ puts opts
23
+ exit
24
+ end
25
+ opts.on("-V", "--version", "Display version") do
26
+ puts VERSION
27
+ exit
28
+ end
29
+ opts.on("-c", "--config FILE", "Sensu JSON config FILE. Default: /etc/sensu/config.json (if exists)") do |file|
30
+ options[:config_file] = file
31
+ end
32
+ opts.on("-d", "--config_dir DIR[,DIR]", "DIR or comma-delimited DIR list for Sensu JSON config files. Default: /etc/sensu/conf.d (if exists)") do |dir|
33
+ options[:config_dirs] = dir.split(",")
34
+ end
35
+ opts.on("--validate_config", "Validate the compiled configuration and exit") do
36
+ options[:validate_config] = true
37
+ end
38
+ opts.on("-P", "--print_config", "Print the compiled configuration and exit") do
39
+ options[:print_config] = true
40
+ end
41
+ opts.on("-e", "--extension_dir DIR", "DIR for Sensu extensions") do |dir|
42
+ options[:extension_dir] = dir
43
+ end
44
+ opts.on("-l", "--log FILE", "Log to a given FILE. Default: STDOUT") do |file|
45
+ options[:log_file] = file
46
+ end
47
+ opts.on("-L", "--log_level LEVEL", "Log severity LEVEL") do |level|
48
+ log_level = level.to_s.downcase.to_sym
49
+ unless Logger::LEVELS.include?(log_level)
50
+ puts "Unknown log level: #{level}"
51
+ exit 1
52
+ end
53
+ options[:log_level] = log_level
54
+ end
55
+ opts.on("-v", "--verbose", "Enable verbose logging") do
56
+ options[:log_level] = :debug
57
+ end
58
+ opts.on("-b", "--background", "Fork into the background") do
59
+ options[:daemonize] = true
60
+ end
61
+ opts.on("-p", "--pid_file FILE", "Write the PID to a given FILE") do |file|
62
+ options[:pid_file] = file
63
+ end
64
+ end
65
+ optparse.parse!(arguments)
66
+ options
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,217 @@
1
+ require "base64"
2
+ require "em-http-server"
3
+ require "sensu/json"
4
+ require "sensu/utilities"
5
+ require "sensu/api/utilities/transport_info"
6
+ require "sensu/client/utils"
7
+
8
+ module Sensu
9
+ module Client
10
+ # EventMachine connection handler for the Sensu HTTP client's socket.
11
+ #
12
+ # The Sensu client listens on localhost, port 3031 (by default), for
13
+ # TCP HTTP connections. This allows software running on the host to
14
+ # push check results (that may contain metrics) into Sensu, without
15
+ # needing to know anything about Sensu's internal implementation.
16
+ #
17
+ # All requests and responses expect a json-encoded body (if a body
18
+ # is expected at all).
19
+ #
20
+ # This socket requires receiving a proper HTTP request to any of
21
+ # the following endpoints:
22
+ #
23
+ # GET /info
24
+ # This endpoint returns 200 OK with some basic Sensu info
25
+ #
26
+ # POST /results
27
+ # This endpoint expects application/json body with a check result
28
+ #
29
+ # GET /settings
30
+ # This endpoint responds with 200 OK and the Sensu configuration
31
+ #
32
+ # GET /brew
33
+ # This endpoint gets you some fresh coffee
34
+ class HTTPSocket < EM::HttpServer::Server
35
+ include Sensu::API::Utilities::TransportInfo
36
+ include Utilities
37
+ include CheckUtils
38
+
39
+ attr_accessor :logger, :settings, :transport
40
+
41
+ def initialize
42
+ super
43
+ @endpoints = {
44
+ "/info" => {
45
+ "methods" => {
46
+ "GET" => method(:process_request_info)
47
+ },
48
+ "help" => "Sensu client information"
49
+ },
50
+ "/results" => {
51
+ "methods" => {
52
+ "POST" => method(:process_request_results)
53
+ },
54
+ "help" => "Send check JSON results here"
55
+ },
56
+ "/settings" => {
57
+ "methods" => {
58
+ "GET" => method(:process_request_settings)
59
+ },
60
+ "help" => "Get redacted Sensu settings (requires basic auth). Use ?redacted=false if you want the setting unredacted."
61
+ },
62
+ "/brew" => {
63
+ "methods" => {
64
+ "GET" => Proc.new { |response|
65
+ send_response(418, "I'm a teapot", {
66
+ :response => "I'm a teapot!"
67
+ })
68
+ }
69
+ },
70
+ "help" => "Ask Sensu to brew a cup of joe (try it!)"
71
+ }
72
+ }
73
+ @response = nil
74
+ end
75
+
76
+ def authorized?
77
+ http_options = @settings[:client][:http_socket] || Hash.new
78
+ if http_options[:user] and http_options[:password]
79
+ if @http[:authorization]
80
+ scheme, base64 = @http[:authorization].split("\s")
81
+ if scheme == "Basic"
82
+ user, password = Base64.decode64(base64).split(":")
83
+ return (user == http_options[:user] && password == http_options[:password])
84
+ end
85
+ end
86
+ end
87
+ false
88
+ end
89
+
90
+ def unauthorized_response
91
+ @logger.warn("http socket refusing to serve unauthorized request")
92
+ @response.headers["WWW-Authenticate"] = 'Basic realm="Sensu Client Restricted Area"'
93
+ send_response(401, "Unauthorized", {
94
+ :response => "You must be authenticated using your http_options user and password settings"
95
+ })
96
+ end
97
+
98
+ def send_response(status, status_string, content)
99
+ @logger.debug("http socket sending response", {
100
+ :status => status,
101
+ :status_string => status_string,
102
+ :content => content
103
+ })
104
+ @response.status = status
105
+ @response.status_string = status_string
106
+ @response.content = Sensu::JSON::dump(content)
107
+ @response.send_response
108
+ end
109
+
110
+ def process_request_info
111
+ if @settings[:client][:http_socket] &&
112
+ @settings[:client][:http_socket][:protect_all_endpoints]
113
+ return unauthorized_response unless authorized?
114
+ end
115
+ transport_info do |info|
116
+ send_response(200, "OK", {
117
+ :sensu => {
118
+ :version => VERSION
119
+ },
120
+ :transport => info
121
+ })
122
+ end
123
+ end
124
+
125
+ def process_request_results
126
+ if @settings[:client][:http_socket] &&
127
+ @settings[:client][:http_socket][:protect_all_endpoints]
128
+ return unauthorized_response unless authorized?
129
+ end
130
+ if @http[:content_type] and @http[:content_type].include?("application/json") and @http_content
131
+ begin
132
+ object = Sensu::JSON::load(@http_content)
133
+ if object.is_a?(Array)
134
+ object.each do |check|
135
+ process_check_result(check)
136
+ end
137
+ else
138
+ process_check_result(object)
139
+ end
140
+ send_response(202, "OK", {:response => "ok"})
141
+ rescue Sensu::JSON::ParseError, ArgumentError
142
+ send_response(400, "Failed to parse JSON body", {:response => "Failed to parse JSON body"})
143
+ end
144
+ else
145
+ send_response(415, "Only application/json content type accepted", {:response => "Invalid content type"})
146
+ end
147
+ end
148
+
149
+ def process_request_settings
150
+ return unauthorized_response unless authorized?
151
+ @logger.info("http socket responding to request for configuration settings")
152
+ if @http_query_string and @http_query_string.downcase.include?("redacted=false")
153
+ send_response(200, "OK", @settings.to_hash)
154
+ else
155
+ send_response(200, "OK", redact_sensitive(@settings.to_hash))
156
+ end
157
+ end
158
+
159
+ def http_request_errback(error)
160
+ @logger.error("http socket error while processing request", {
161
+ :error => error.to_s,
162
+ :backtrace => error.backtrace
163
+ })
164
+ @response = EM::DelegatedHttpResponse.new(self)
165
+ @response.content_type "application/json"
166
+ send_response(500, "Internal Server Error", {
167
+ "response" => "Internal Server Error: Check your Sensu logs for error details"
168
+ })
169
+ end
170
+
171
+ # This method is called to process HTTP requests
172
+ def process_http_request
173
+ @logger.debug("http socket processing", {
174
+ :http_request_method => @http_request_method,
175
+ :http_request_uri => @http_request_uri
176
+ })
177
+ @response = EM::DelegatedHttpResponse.new(self)
178
+ @response.content_type "application/json"
179
+ endpoint = @endpoints[@http_request_uri]
180
+ if endpoint
181
+ @logger.debug("http socket endpoint found", {
182
+ :http_request_uri => @http_request_uri,
183
+ :accepted_methods => endpoint["methods"].keys
184
+ })
185
+ method_name = @http_request_method.upcase
186
+ method_handler = endpoint["methods"][method_name]
187
+ if method_handler
188
+ @logger.debug("http socket executing handler", {
189
+ :method_name => method_name,
190
+ :http_request_uri => @http_request_uri
191
+ })
192
+ method_handler.call
193
+ else
194
+ @logger.debug("http socket method is not allowed for endpoint", {
195
+ :method_name => method_name,
196
+ :http_request_uri => @http_request_uri
197
+ })
198
+ send_response(405, "Method Not Allowed", {
199
+ :response => "Valid methods for this endpoint: #{endpoint['methods'].keys}"
200
+ })
201
+ end
202
+ else
203
+ @logger.warn("http socket unknown endpoint requested", :http_request_uri => @http_request_uri)
204
+ help_response = {
205
+ :endpoints => {}
206
+ }
207
+ @endpoints.each do |key, value|
208
+ help_response[:endpoints][key] ||= Hash.new
209
+ help_response[:endpoints][key]["help"] = value["help"]
210
+ help_response[:endpoints][key]["methods"] = value["methods"].keys
211
+ end
212
+ send_response(404, "Not Found", help_response)
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end