Module: Sensu::Utilities
- Included in:
- 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, 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(hash, 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, 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).
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/sensu/utilities.rb', line 264 def attributes_match?(object, match_attributes, 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, value_two) when value_one.to_s == value_two.to_s true when value_one.is_a?(String) && value_one.start_with?(EVAL_PREFIX) 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.
339 340 341 342 343 344 345 |
# File 'lib/sensu/utilities.rb', line 339 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.
350 351 352 353 354 355 |
# File 'lib/sensu/utilities.rb', line 350 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.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/sensu/utilities.rb', line 234 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.
129 130 131 132 133 134 135 136 |
# File 'lib/sensu/utilities.rb', line 129 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.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/sensu/utilities.rb', line 291 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.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/sensu/utilities.rb', line 316 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.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/sensu/utilities.rb', line 173 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`.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/sensu/utilities.rb', line 203 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(hash, 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 |
# File 'lib/sensu/utilities.rb', line 100 def redact_sensitive(hash, keys=nil) keys ||= %w[ password passwd pass api_key api_token access_key secret_key private_key secret ] hash = hash.dup hash.each do |key, value| if keys.include?(key.to_s) hash[key] = "REDACTED" elsif value.is_a?(Hash) hash[key] = redact_sensitive(value, keys) elsif value.is_a?(Array) hash[key] = value.map do |item| item.is_a?(Hash) ? redact_sensitive(item, keys) : item end end end hash 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.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/sensu/utilities.rb', line 146 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 |