module Chef::EncryptedAttribute::SearchHelper
Search Helpers to do normal or partial searches.
Public Instance Methods
Assert that the normal (no partial) search response is correct.
@param resp [Array] normal search result. @return void @raise [SearchFatalError] if the Chef
search response is wrong. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 190 def assert_normal_search_response(resp) return if resp.is_a?(Array) fail SearchFatalError, "Wrong response received from Normal Search: #{resp.inspect}" end
Assert that the partial search response is correct.
@param resp [Hash] partial search result. For example:
`{ 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }`.
@return void @raise [SearchFatalError] if the Chef
search response is wrong. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 288 def assert_partial_search_response(resp) return if resp.is_a?(Hash) && resp.key?('rows') && resp['rows'].is_a?(Array) fail SearchFatalError, "Wrong response received from Partial Search: #{resp.inspect}" end
Assert that the search keys structure format is correct.
@return void @raise [InvalidSearchKeys] if search keys structure is wrong. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 108 def assert_search_keys(keys) return if valid_search_keys?(keys) fail InvalidSearchKeys, "Invalid search keys: #{keys.inspect}" end
Translates Chef
HTTP exceptions to search exceptions.
@yield [] the block doing the Chef
Search. @return [Mixed] the value returned by the block. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 128 def catch_search_exceptions(&block) block.call rescue Net::HTTPServerException => e unless e.response.is_a?(Net::HTTPResponse) && e.response.code == '404' raise SearchFailure, "Search exception #{e.class}: #{e}" end return [] rescue Net::HTTPFatalError => e raise SearchFailure, "Search exception #{e.class}: #{e}" end
Check if search query is empty.
@param query [Array<String>, String] search query. @return [Boolean] `true` if search query is empty. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 118 def empty_search?(query) query.is_a?(String) && query.empty? || query.is_a?(Array) && query.count == 0 end
Escapes a search query string to be used in URLs.
@param str [String] query to escape. @return [String] escaped query string. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 43 def escape(str) URI.escape(str.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) end
Escapes a search query array.
When multiple queries are provided, the result will be OR-ed.
@param query [Array<String>, String] search query. @return [String] escaped query string.
# File lib/chef/encrypted_attribute/search_helper.rb, line 53 def escape_query(query) query_s = if query.is_a?(Array) query.map { |item| "( #{item} )" }.compact.join(' OR ') else query.to_s end escape(query_s) end
Filters normal search results that do not correspond to the searched node.
Used when searching by node name.
@param resp [Array] normal search result. @param name [String, nil] searched node name. @return [Array] The search result removing the filtered results. @raise [SearchFatalError] if more than one result is returned when
searching by node name.
@api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 224 def filter_normal_search_response(resp, name) return resp if name.nil? resp.select { |row| row.name == name }.tap do |r| fail SearchFatalError, 'Multiple responses received from Partial Search:'\ " #{r.inspect}" if r.count > 1 end end
Filters partial search results that do not correspond to the searched node.
Used when searching by node name.
@param resp [Hash] partial search result. For example:
`{ 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }`.
@param name [String, nil] searched node name. @return [Hash] The search result removing the filtered results. @raise [SearchFatalError] if more than one result is returned when
searching by node name.
@api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 322 def filter_partial_search_response(resp, name) return resp if name.nil? filtered_resp = resp.select do |row| row['data']['name'] == name end filtered_resp.tap do |r| fail SearchFatalError, 'Multiple responses received from Partial Search:'\ " #{r.inspect}" if r.count > 1 end end
Adds the `name` key to the search keys structure.
Used to get the node name when searching nodes by name.
@param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@return [Hash] the search keys structure including the `name` key. For
example: `{ipaddress: %w(ipaddress), mysql_version: %w(mysql version), name: %w(name) }`.
@api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 306 def generate_partial_search_keys(keys) keys.merge('name' => %w(name)) end
Does a normal (no partial) search in the Chef
Server.
@param type [Symbol] search index to use. See [Chef Search Indexes]
(http://docs.chef.io/chef_search.html#search-indexes).
@param name [String, nil] searched node name. @param query [String, Array<String>] search query. For example:
`%w(admin:true)`. Results will be *OR*-ed when multiple string queries are provided.
@param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@param rows [Fixnum, String] maximum number of rows to return. @return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [InvalidSearchKeys] if search keys structure is wrong. @raise [SearchFatalError] if more than one result is returned when
searching by node name.
# File lib/chef/encrypted_attribute/search_helper.rb, line 269 def normal_search(type, name, query, keys, rows = 1000) escaped_query = escape_query(query) Chef::Log.info( "Normal Search query: #{escaped_query}, keys: #{keys.inspect}" ) assert_search_keys(keys) resp = self.query.search(type, escaped_query, nil, 0, rows)[0] assert_normal_search_response(resp) parse_normal_search_response(resp, keys, name) end
Parses a normal (no partial) full search search response.
@param resp [Array] normal search result. @param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@param name [String, nil] searched node name. @return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [SearchFatalError] if more than one result is returned when
searching by node name.
@api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 244 def parse_normal_search_response(resp, keys, name) filter_normal_search_response(resp, name).map do |row| Hash[keys.map do |key_name, attr_ary| value = parse_normal_search_row_attribute(row, attr_ary) [key_name, value] end] end end
Parses a normal (no partial) search response row.
@param row [Array] the normal search result row. @param attr_ary [Array<String>] key path as Array. @return [Hash] A hash with the response row, for example:
`[ 'ipaddress' => '192.168.1.1' }`
@api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 203 def parse_normal_search_row_attribute(row, attr_ary) attr_ary.reduce(row) do |r, attr| if r.respond_to?(attr) r.send(attr) elsif r.respond_to?(:key?) r[attr.to_s] if r.key?(attr.to_s) end end end
Parses a partial full search search response.
@param resp [Hash] partial search result. For example:
`{ 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }`.
@param name [String, nil] searched node name. @param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [SearchFatalError] if the Chef
search response is wrong. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 345 def parse_partial_search_response(resp, name, keys) filter_partial_search_response(resp['rows'], name).map do |row| if row.is_a?(Hash) && row['data'].is_a?(Hash) row['data'].tap { |r| r.delete('name') unless keys.key?('name') } else fail SearchFatalError, "Wrong row format received from Partial Search: #{row.inspect}" end end.compact end
Does a partial search in the Chef
Server.
@param type [Symbol] search index to use. See [Chef Search Indexes]
(http://docs.chef.io/chef_search.html#search-indexes).
@param name [String, nil] searched node name. @param query [String, Array<String>] search query. For example:
`%w(admin:true)`. Results will be *OR*-ed when multiple string queries are provided.
@param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@param rows [Fixnum, String] maximum number of rows to return. @return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [InvalidSearchKeys] if search keys structure is wrong. @raise [SearchFatalError] if the Chef
search response is wrong.
# File lib/chef/encrypted_attribute/search_helper.rb, line 371 def partial_search(type, name, query, keys, rows = 1000) escaped_query = "search/#{escape(type)}?q=#{escape_query(query)}&start=0&rows=#{rows}" Chef::Log.info( "Partial Search query: #{escaped_query}, keys: #{keys.inspect}" ) assert_search_keys(keys) rest = Chef::ServerAPI.new(Chef::Config[:chef_server_url]) resp = rest.post(escaped_query, generate_partial_search_keys(keys)) assert_partial_search_response(resp) parse_partial_search_response(resp, name, keys) end
Gets a Chef
Search Query object.
@return [Chef::Search::Query] search query object instance. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 34 def query Chef::Search::Query.new end
Does a search in the Chef
Server.
@param type [Symbol] search index to use. See [Chef Search Indexes]
(http://docs.chef.io/chef_search.html#search-indexes).
@param query [Array<String>, String] search query. For example:
`%w(admin:true)`. Results will be *OR*-ed when multiple string queries are provided.
@param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@param rows [Fixnum, String] maximum number of rows to return. @param partial_search
[Boolean] whether to use partial search. @return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [SearchFailure] if there is a Chef
search error. @raise [SearchFatalError] if the Chef
search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong.
# File lib/chef/encrypted_attribute/search_helper.rb, line 155 def search(type, query, keys, rows = 1000, partial_search = true) return [] if empty_search?(query) # avoid empty searches search_method = partial_search ? :partial_search : :normal_search catch_search_exceptions do send(search_method, type, nil, query, keys, rows) end end
Does a search in the Chef
Server by node or client name.
@param type [Symbol] search index to use. See [Chef Search Indexes]
(http://docs.chef.io/chef_search.html#search-indexes).
@param name [String] node name to search. @param keys [Hash] search keys structure. For example:
`{ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }`.
@param rows [Fixnum, String] maximum number of rows to return. @param partial_search
[Boolean] whether to use partial search. @return [Array<Hash>] An array with the response, for example:
`[{ 'ipaddress' => '192.168.1.1' }]`
@raise [SearchFailure] if there is a Chef
search error. @raise [SearchFatalError] if the Chef
search response is wrong. @raise [InvalidSearchKeys] if search keys structure is wrong.
# File lib/chef/encrypted_attribute/search_helper.rb, line 177 def search_by_name(type, name, keys, rows = 1000, partial_search = true) search_method = partial_search ? :partial_search : :normal_search catch_search_exceptions do send(search_method, type, name, "name:#{name}", keys, rows) end end
Checks if a search keys structure format is correct.
This is an example of a correct search structure:
“`ruby {
ipaddress: %w(ipaddress), mysql_version: %w(mysql version)
} “`
@param keys [Hash] search keys structure. @return [Boolean] `true` if search keys structure format is correct. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 96 def valid_search_keys?(keys) return false unless keys.is_a?(Hash) keys.reduce(true) do |r, (k, v)| r && valid_search_keys_key?(k) && valid_search_keys_value?(v) end end
Checks if a Hash key from a search keys structure format is correct.
@param k [Mixed] hash key to check. @return [Boolean] `true` if key is a `String` or a `Symbol`. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 68 def valid_search_keys_key?(k) k.is_a?(String) || k.is_a?(Symbol) end
Checks if a Hash value from a search keys structure format is correct.
@param v [Mixed] hash value to check. @return [Boolean] `true` if value is a `Array<String>`. @api private
# File lib/chef/encrypted_attribute/search_helper.rb, line 77 def valid_search_keys_value?(v) return false unless v.is_a?(Array) v.reduce(true) { |a, e| a && e.is_a?(String) } end