Module: Sensu::Utilities
- Included in:
- API::Utilities::FilterResponseContent, API::Utilities::PublishCheckRequest, Client::HTTPSocket, Daemon
- Defined in:
- lib/sensu/utilities.rb
Constant Summary collapse
- EVAL_PREFIX =
"eval:".freeze
Instance Method Summary collapse
-
#attributes_match?(object, match_attributes, support_eval = true, object_attributes = nil) ⇒ TrueClass, FalseClass
Determine if all attribute values match those of the corresponding object attributes.
-
#check_subdued?(check) ⇒ TrueClass, FalseClass
Determine if a check is subdued, by conditions set in the check definition.
-
#deep_dup(obj) ⇒ obj
Creates a deep dup of basic ruby objects with support for walking hashes and arrays.
-
#deep_merge(hash_one, hash_two) ⇒ Hash
Deep merge two hashes.
-
#determine_check_cron_time(check) ⇒ Object
Determine the next check cron time.
-
#eval_attribute_value(object, raw_eval_string, raw_value) ⇒ TrueClass, FalseClass
Ruby ‘eval()` a string containing an expression, within the scope/context of a sandbox.
-
#find_attribute_value(tree, path, default) ⇒ Object
Traverse a hash for an attribute value, with a fallback default value if nil.
-
#in_time_window?(condition) ⇒ TrueClass, FalseClass
Determine if the current time falls within a time window.
-
#in_time_windows?(conditions) ⇒ TrueClass, FalseClass
Determine if time window conditions for one or more days of the week are met.
-
#object_substitute_tokens(object, attributes) ⇒ Array
Perform token substitution for an object.
-
#process_cpu_times(&callback) ⇒ Array
Retrieve the process CPU times.
-
#process_eval_string(object, raw_eval_string) ⇒ String
Process an eval attribute value, a Ruby ‘eval()` string containing an expression to be evaluated within the scope/context of a sandbox.
-
#random_uuid ⇒ String
Generate a random universally unique identifier.
-
#redact_sensitive(obj, keys = nil) ⇒ Hash
Remove sensitive information from a hash (eg. passwords).
-
#retry_until_true(wait = 0.5, &block) ⇒ Object
Retry a code block until it retures true.
-
#substitute_tokens(tokens, attributes) ⇒ Array
Substitute dot notation tokens (eg. :::db.name|production:::) with the associated definition attribute value.
-
#system_address ⇒ String
Retrieve the system IP address.
-
#system_hostname ⇒ String
Retrieve the system hostname.
-
#testing? ⇒ TrueClass, FalseClass
Determine if Sensu is being tested, using the process name.
Instance Method Details
#attributes_match?(object, match_attributes, support_eval = true, object_attributes = nil) ⇒ TrueClass, FalseClass
Determine if all attribute values match those of the corresponding object attributes. Attributes match if the value objects are equivalent, are both hashes with matching key/value pairs (recursive), have equal string values, or evaluate to true (Ruby eval).
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/sensu/utilities.rb', line 304 def attributes_match?(object, match_attributes, support_eval=true, object_attributes=nil) object_attributes ||= object match_attributes.all? do |key, value_one| value_two = object_attributes[key] case when value_one == value_two true when value_one.is_a?(Hash) && value_two.is_a?(Hash) attributes_match?(object, value_one, support_eval, value_two) when value_one.to_s == value_two.to_s true when value_one.is_a?(String) && value_one.start_with?(EVAL_PREFIX) && support_eval eval_attribute_value(object, value_one, value_two) else false end end end |
#check_subdued?(check) ⇒ TrueClass, FalseClass
Determine if a check is subdued, by conditions set in the check definition. If any of the conditions are true, without an exception, the check is subdued.
379 380 381 382 383 384 385 |
# File 'lib/sensu/utilities.rb', line 379 def check_subdued?(check) if check[:subdue] in_time_windows?(check[:subdue]) else false end end |
#deep_dup(obj) ⇒ obj
Creates a deep dup of basic ruby objects with support for walking hashes and arrays.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/sensu/utilities.rb', line 59 def deep_dup(obj) if obj.class == Hash new_obj = obj.dup new_obj.each do |key, value| new_obj[deep_dup(key)] = deep_dup(value) end new_obj elsif obj.class == Array arr = [] obj.each do |item| arr << deep_dup(item) end arr elsif obj.class == String obj.dup else obj end end |
#deep_merge(hash_one, hash_two) ⇒ Hash
Deep merge two hashes. Nested hashes are deep merged, arrays are concatenated and duplicate array items are removed.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/sensu/utilities.rb', line 39 def deep_merge(hash_one, hash_two) merged = hash_one.dup hash_two.each do |key, value| merged[key] = case when hash_one[key].is_a?(Hash) && value.is_a?(Hash) deep_merge(hash_one[key], value) when hash_one[key].is_a?(Array) && value.is_a?(Array) (hash_one[key] + value).uniq else value end end merged end |
#determine_check_cron_time(check) ⇒ Object
Determine the next check cron time.
390 391 392 393 394 395 |
# File 'lib/sensu/utilities.rb', line 390 def determine_check_cron_time(check) cron_parser = CronParser.new(check[:cron]) current_time = Time.now next_cron_time = cron_parser.next(current_time) next_cron_time - current_time end |
#eval_attribute_value(object, raw_eval_string, raw_value) ⇒ TrueClass, FalseClass
Ruby ‘eval()` a string containing an expression, within the scope/context of a sandbox. This method is for attribute values starting with “eval:”, with the Ruby expression following the colon. A single variable is provided to the expression, `value`, equal to the corresponding object attribute value. Dot notation tokens in the expression, e.g. `:::mysql.user:::`, are substituted with the corresponding object attribute values prior to evaluation. The expression is expected to return a boolean value.
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/sensu/utilities.rb', line 273 def eval_attribute_value(object, raw_eval_string, raw_value) eval_string = process_eval_string(object, raw_eval_string) unless eval_string.nil? begin value = Marshal.load(Marshal.dump(raw_value)) !!Sandbox.eval(eval_string, value) rescue StandardError, SyntaxError => error @logger.error("attribute value eval error", { :object => object, :raw_eval_string => raw_eval_string, :raw_value => raw_value, :error => error.to_s }) false end else false end end |
#find_attribute_value(tree, path, default) ⇒ Object
Traverse a hash for an attribute value, with a fallback default value if nil.
168 169 170 171 172 173 174 175 |
# File 'lib/sensu/utilities.rb', line 168 def find_attribute_value(tree, path, default) attribute = tree[path.shift] if attribute.is_a?(Hash) find_attribute_value(attribute, path, default) else attribute.nil? ? default : attribute end end |
#in_time_window?(condition) ⇒ TrueClass, FalseClass
Determine if the current time falls within a time window. The provided condition must have a ‘:begin` and `:end` time, eg. “11:30:00 PM”, or `false` will be returned.
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/sensu/utilities.rb', line 331 def in_time_window?(condition) if condition.has_key?(:begin) && condition.has_key?(:end) begin_time = Time.parse(condition[:begin]) end_time = Time.parse(condition[:end]) if end_time < begin_time if Time.now < end_time begin_time = Time.new(*begin_time.strftime("%Y %m %d 00 00 00 %:z").split("\s")) else end_time = Time.new(*end_time.strftime("%Y %m %d 23 59 59 %:z").split("\s")) end end Time.now >= begin_time && Time.now <= end_time else false end end |
#in_time_windows?(conditions) ⇒ TrueClass, FalseClass
Determine if time window conditions for one or more days of the week are met. If a day of the week is provided, it can provide one or more conditions, each with a ‘:begin` and `:end` time, eg. “11:30:00 PM”, or `false` will be returned.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/sensu/utilities.rb', line 356 def in_time_windows?(conditions) in_window = false window_days = conditions[:days] || {} if window_days[:all] in_window = window_days[:all].any? do |condition| in_time_window?(condition) end end current_day = Time.now.strftime("%A").downcase.to_sym if !in_window && window_days[current_day] in_window = window_days[current_day].any? do |condition| in_time_window?(condition) end end in_window end |
#object_substitute_tokens(object, attributes) ⇒ Array
Perform token substitution for an object. String values are passed to ‘substitute_tokens()`, arrays and sub-hashes are processed recursively. Numeric values are ignored.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/sensu/utilities.rb', line 212 def object_substitute_tokens(object, attributes) unmatched_tokens = [] case object when Hash object.each do |key, value| object[key], unmatched = object_substitute_tokens(value, attributes) unmatched_tokens.push(*unmatched) end when Array object.map! do |value| value, unmatched = object_substitute_tokens(value, attributes) unmatched_tokens.push(*unmatched) value end when String object, unmatched_tokens = substitute_tokens(object, attributes) end [object, unmatched_tokens.uniq] end |
#process_cpu_times(&callback) ⇒ Array
Retrieve the process CPU times. If the cpu times cannot be determined and an error is thrown, ‘[nil, nil, nil, nil]` will be returned.
103 104 105 106 107 108 |
# File 'lib/sensu/utilities.rb', line 103 def process_cpu_times(&callback) determine_cpu_times = Proc.new do ::Process.times.to_a rescue [nil, nil, nil, nil] end EM::defer(determine_cpu_times, callback) end |
#process_eval_string(object, raw_eval_string) ⇒ String
Process an eval attribute value, a Ruby ‘eval()` string containing an expression to be evaluated within the scope/context of a sandbox. This methods strips away the expression prefix, `eval:`, and substitues any dot notation tokens with the corresponding event data values. If there are unmatched tokens, this method will return `nil`.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/sensu/utilities.rb', line 242 def process_eval_string(object, raw_eval_string) eval_string = raw_eval_string.slice(5..-1) eval_string, unmatched_tokens = substitute_tokens(eval_string, object) if unmatched_tokens.empty? eval_string else @logger.error("attribute value eval unmatched tokens", { :object => object, :raw_eval_string => raw_eval_string, :unmatched_tokens => unmatched_tokens }) nil end end |
#random_uuid ⇒ String
Generate a random universally unique identifier.
113 114 115 |
# File 'lib/sensu/utilities.rb', line 113 def random_uuid ::SecureRandom.uuid end |
#redact_sensitive(obj, keys = nil) ⇒ Hash
Remove sensitive information from a hash (eg. passwords). By default, hash values will be redacted for the following keys: password, passwd, pass, api_key, api_token, access_key, secret_key, private_key, secret
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/sensu/utilities.rb', line 125 def redact_sensitive(obj, keys=nil) keys ||= %w[ password passwd pass api_key api_token access_key secret_key private_key secret routing_key access_token_read access_token_write access_token_path webhook_url nickserv_password channel_password community keystore_password truststore_password proxy_password access_key_id secret_access_key ] obj = obj.dup if obj.is_a?(Hash) obj.each do |key, value| if keys.include?(key.to_s) obj[key] = "REDACTED" elsif value.is_a?(Hash) || value.is_a?(Array) obj[key] = redact_sensitive(value, keys) end end elsif obj.is_a?(Array) obj.map! do |item| if item.is_a?(Hash) || item.is_a?(Array) redact_sensitive(item, keys) else item end end end obj end |
#retry_until_true(wait = 0.5, &block) ⇒ Object
Retry a code block until it retures true. The first attempt and following retries are delayed.
25 26 27 28 29 30 31 |
# File 'lib/sensu/utilities.rb', line 25 def retry_until_true(wait=0.5, &block) EM::Timer.new(wait) do unless block.call retry_until_true(wait, &block) end end end |
#substitute_tokens(tokens, attributes) ⇒ Array
Substitute dot notation tokens (eg. :::db.name|production:::) with the associated definition attribute value. Tokens can provide a fallback default value, following a pipe.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/sensu/utilities.rb', line 185 def substitute_tokens(tokens, attributes) unmatched_tokens = [] encoded_tokens = tokens.encode("UTF-8", "binary", { :invalid => :replace, :undef => :replace, :replace => "" }) substituted = encoded_tokens.gsub(/:::([^:].*?):::/) do token, default = $1.to_s.split("|", 2) path = token.split(".").map(&:to_sym) matched = find_attribute_value(attributes, path, default) if matched.nil? unmatched_tokens << token end matched end [substituted, unmatched_tokens] end |
#system_address ⇒ String
Retrieve the system IP address. If a valid non-loopback IPv4 address cannot be found and an error is thrown, ‘nil` will be returned.
92 93 94 95 96 |
# File 'lib/sensu/utilities.rb', line 92 def system_address ::Socket.ip_address_list.find { |address| address.ipv4? && !address.ipv4_loopback? }.ip_address rescue nil end |
#system_hostname ⇒ String
Retrieve the system hostname. If the hostname cannot be determined and an error is thrown, ‘nil` will be returned.
83 84 85 |
# File 'lib/sensu/utilities.rb', line 83 def system_hostname ::Socket.gethostname rescue nil end |
#testing? ⇒ TrueClass, FalseClass
Determine if Sensu is being tested, using the process name. Sensu is being test if the process name is “rspec”,
16 17 18 |
# File 'lib/sensu/utilities.rb', line 16 def testing? File.basename($0) == "rspec" end |