Module: Chef::EncryptedAttribute::SearchHelper

Extended by:
SearchHelper
Included in:
RemoteClients, RemoteNode, RemoteNodes, SearchHelper
Defined in:
lib/chef/encrypted_attribute/search_helper.rb

Overview

Search Helpers to do normal or partial searches.

Instance Method Summary collapse

Instance Method Details

#assert_normal_search_response(resp) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Assert that the normal (no partial) search response is correct.

Parameters:

  • resp (Array)

    normal search result.

Returns:

  • void

Raises:


190
191
192
193
194
# 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_partial_search_response(resp) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Assert that the partial search response is correct.

Parameters:

  • resp (Hash)

    partial search result. For example: { 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }.

Returns:

  • void

Raises:


288
289
290
291
292
293
# 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_search_keys(keys) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Assert that the search keys structure format is correct.

Returns:

  • void

Raises:


108
109
110
111
# 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

#catch_search_exceptions { ... } ⇒ Mixed

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Translates Chef HTTP exceptions to search exceptions.

Yields:

  • [] the block doing the Chef Search.

Returns:

  • (Mixed)

    the value returned by the block.


128
129
130
131
132
133
134
135
136
137
# 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

#empty_search?(query) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if search query is empty.

Parameters:

  • query (Array<String>, String)

    search query.

Returns:

  • (Boolean)

    true if search query is empty.


118
119
120
121
# 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

#escape(str) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Escapes a search query string to be used in URLs.

Parameters:

  • str (String)

    query to escape.

Returns:

  • (String)

    escaped query string.


43
44
45
# File 'lib/chef/encrypted_attribute/search_helper.rb', line 43

def escape(str)
  URI.escape(str.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
end

#escape_query(query) ⇒ String

Escapes a search query array.

When multiple queries are provided, the result will be OR-ed.

Parameters:

  • query (Array<String>, String)

    search query.

Returns:

  • (String)

    escaped query string.


53
54
55
56
57
58
59
60
61
# 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

#filter_normal_search_response(resp, name) ⇒ Array

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Filters normal search results that do not correspond to the searched node.

Used when searching by node name.

Parameters:

  • resp (Array)

    normal search result.

  • name (String, nil)

    searched node name.

Returns:

  • (Array)

    The search result removing the filtered results.

Raises:

  • (SearchFatalError)

    if more than one result is returned when searching by node name.


224
225
226
227
228
229
230
231
# 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

#filter_partial_search_response(resp, name) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Filters partial search results that do not correspond to the searched node.

Used when searching by node name.

Parameters:

  • resp (Hash)

    partial search result. For example: { 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }.

  • name (String, nil)

    searched node name.

Returns:

  • (Hash)

    The search result removing the filtered results.

Raises:

  • (SearchFatalError)

    if more than one result is returned when searching by node name.


322
323
324
325
326
327
328
329
330
331
332
# 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

#generate_partial_search_keys(keys) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds the name key to the search keys structure.

Used to get the node name when searching nodes by name.

Parameters:

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

Returns:

  • (Hash)

    the search keys structure including the name key. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version), name: %w(name) }.


306
307
308
# File 'lib/chef/encrypted_attribute/search_helper.rb', line 306

def generate_partial_search_keys(keys)
  keys.merge('name' => %w(name))
end

#normal_search(type, name, query, keys, rows = 1000) ⇒ Array<Hash>

Does a normal (no partial) search in the Chef Server.

Parameters:

  • type (Symbol)

    search index to use. See Chef Search Indexes.

  • name (String, nil)

    searched node name.

  • query (String, Array<String>)

    search query. For example: %w(admin:true). Results will be OR-ed when multiple string queries are provided.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

  • rows (Fixnum, String) (defaults to: 1000)

    maximum number of rows to return.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:


269
270
271
272
273
274
275
276
277
278
279
# 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

#parse_normal_search_response(resp, keys, name) ⇒ Array<Hash>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses a normal (no partial) full search search response.

Parameters:

  • resp (Array)

    normal search result.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

  • name (String, nil)

    searched node name.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:

  • (SearchFatalError)

    if more than one result is returned when searching by node name.


244
245
246
247
248
249
250
251
# 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

#parse_normal_search_row_attribute(row, attr_ary) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses a normal (no partial) search response row.

Parameters:

  • row (Array)

    the normal search result row.

  • attr_ary (Array<String>)

    key path as Array.

Returns:

  • (Hash)

    A hash with the response row, for example: [ 'ipaddress' => '192.168.1.1' }


203
204
205
206
207
208
209
210
211
# 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

#parse_partial_search_response(resp, name, keys) ⇒ Array<Hash>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses a partial full search search response.

Parameters:

  • resp (Hash)

    partial search result. For example: { 'rows' => [ 'data' => { 'ipaddress' => '192.168.1.1' } }] }.

  • name (String, nil)

    searched node name.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:


345
346
347
348
349
350
351
352
353
354
# 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

#partial_search(type, name, query, keys, rows = 1000) ⇒ Array<Hash>

Does a partial search in the Chef Server.

Parameters:

  • type (Symbol)

    search index to use. See Chef Search Indexes.

  • name (String, nil)

    searched node name.

  • query (String, Array<String>)

    search query. For example: %w(admin:true). Results will be OR-ed when multiple string queries are provided.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

  • rows (Fixnum, String) (defaults to: 1000)

    maximum number of rows to return.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:


371
372
373
374
375
376
377
378
379
380
381
382
383
# 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

#queryChef::Search::Query

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Gets a Chef Search Query object.

Returns:

  • (Chef::Search::Query)

    search query object instance.


34
35
36
# File 'lib/chef/encrypted_attribute/search_helper.rb', line 34

def query
  Chef::Search::Query.new
end

#search(type, query, keys, rows = 1000, partial_search = true) ⇒ Array<Hash>

Does a search in the Chef Server.

Parameters:

  • type (Symbol)

    search index to use. See Chef Search Indexes.

  • query (Array<String>, String)

    search query. For example: %w(admin:true). Results will be OR-ed when multiple string queries are provided.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

  • rows (Fixnum, String) (defaults to: 1000)

    maximum number of rows to return.

  • partial_search (Boolean) (defaults to: true)

    whether to use partial search.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:


155
156
157
158
159
160
161
# 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

#search_by_name(type, name, keys, rows = 1000, partial_search = true) ⇒ Array<Hash>

Does a search in the Chef Server by node or client name.

Parameters:

  • type (Symbol)

    search index to use. See Chef Search Indexes.

  • name (String)

    node name to search.

  • keys (Hash)

    search keys structure. For example: {ipaddress: %w(ipaddress), mysql_version: %w(mysql version) }.

  • rows (Fixnum, String) (defaults to: 1000)

    maximum number of rows to return.

  • partial_search (Boolean) (defaults to: true)

    whether to use partial search.

Returns:

  • (Array<Hash>)

    An array with the response, for example: [{ 'ipaddress' => '192.168.1.1' }]

Raises:


177
178
179
180
181
182
# 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

#valid_search_keys?(keys) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Checks if a search keys structure format is correct.

This is an example of a correct search structure:

{
  ipaddress: %w(ipaddress),
  mysql_version: %w(mysql version)
}

Parameters:

  • keys (Hash)

    search keys structure.

Returns:

  • (Boolean)

    true if search keys structure format is correct.


96
97
98
99
100
101
# 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

#valid_search_keys_key?(k) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Checks if a Hash key from a search keys structure format is correct.

Parameters:

  • k (Mixed)

    hash key to check.

Returns:

  • (Boolean)

    true if key is a String or a Symbol.


68
69
70
# 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

#valid_search_keys_value?(v) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Checks if a Hash value from a search keys structure format is correct.

Parameters:

  • v (Mixed)

    hash value to check.

Returns:

  • (Boolean)

    true if value is a Array<String>.


77
78
79
80
# 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