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_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).
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/sensu/utilities.rb', line 271 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.
346 347 348 349 350 351 352 |
# File 'lib/sensu/utilities.rb', line 346 def check_subdued?(check) if check[:subdue] in_time_windows?(check[:subdue]) else false 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.
357 358 359 360 361 362 |
# File 'lib/sensu/utilities.rb', line 357 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.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/sensu/utilities.rb', line 240 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.
135 136 137 138 139 140 141 142 |
# File 'lib/sensu/utilities.rb', line 135 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.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/sensu/utilities.rb', line 298 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.
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/sensu/utilities.rb', line 323 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.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/sensu/utilities.rb', line 179 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.
78 79 80 81 82 83 |
# File 'lib/sensu/utilities.rb', line 78 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`.
209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/sensu/utilities.rb', line 209 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.
88 89 90 |
# File 'lib/sensu/utilities.rb', line 88 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
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/sensu/utilities.rb', line 100 def redact_sensitive(obj, keys=nil) keys ||= %w[ password passwd pass api_key api_token access_key secret_key private_key secret ] 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.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/sensu/utilities.rb', line 152 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("|", -1) 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.
67 68 69 70 71 |
# File 'lib/sensu/utilities.rb', line 67 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.
58 59 60 |
# File 'lib/sensu/utilities.rb', line 58 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 |