class Object

Constants

LOG_LEVEL

Ohai::Config defines its own log_level and log_location. When loaded, it will override the default ChefConfig::Config values. We save them here before loading ohai/config so that we can override them again inside Chef::Config.

REMOVEME once these configurables are removed from the top level of Ohai.

LOG_LOCATION
Mash

For historical reasons we inject Mash directly into the top level class namespace

MonoLogger
TIMEOUT_OPTS
TIMEOUT_REGEX
WEEKDAYS

Public Instance Methods

api_connection() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 350
def api_connection
  Chef.run_context.transport.connection
end
bury(path, value) click to toggle source

Create nested hashes from JMESPath syntax.

# File lib/chef/resource/_rest_resource.rb, line 373
def bury(path, value)
  raise TypeError unless path.is_a?(String)

  arr = path.split(".")
  ret = {}

  if arr.count == 1
    ret[arr.first] = value

    ret
  else
    partial_path = arr[0..-2].join(".")

    bury(partial_path, bury(arr.last, value))
  end
end
changed_value(property) click to toggle source

Return changed value or nil for delta current->new

# File lib/chef/resource/_rest_resource.rb, line 115
def changed_value(property)
  new_value = new_resource.send(property)
  return new_value if current_resource.nil?

  current_value = current_resource.send(property)

  return current_value if required_properties.include? property

  new_value == current_value ? nil : new_value
end
conditionally_require_on_setting(property, dependent_properties) click to toggle source

Convenience method for conditional requires

# File lib/chef/resource/_rest_resource.rb, line 187
def conditionally_require_on_setting(property, dependent_properties)
  dependent_properties = Array(dependent_properties)

  requirements.assert(:configure) do |a|
    a.assertion do
      # Needs to be set and truthy to require dependent properties
      if new_resource.send(property)
        dependent_properties.all? { |dep_prop| new_resource.property_is_set?(dep_prop) }
      else
        true
      end
    end

    message = format("Setting property :%<property>s requires properties :%<properties>s to be set as well on resource %<resource_name>s",
                     property: property,
                     properties: dependent_properties.join(", :"),
                     resource_name: current_resource.to_s)

    a.failure_message message
  end
end
deep_compact!(hsh) click to toggle source

Remove all empty keys (recursively) from Hash. @see stackoverflow.com/questions/56457020/#answer-56458673

# File lib/chef/resource/_rest_resource.rb, line 356
def deep_compact!(hsh)
  raise TypeError unless hsh.is_a? Hash

  hsh.each do |_, v|
    deep_compact!(v) if v.is_a? Hash
  end.reject! { |_, v| v.nil? || (v.respond_to?(:empty?) && v.empty?) }
end
deep_merge!(hsh1, hsh2) click to toggle source

Deep merge two hashes @see stackoverflow.com/questions/41109599#answer-41109737

# File lib/chef/resource/_rest_resource.rb, line 366
def deep_merge!(hsh1, hsh2)
  raise TypeError unless hsh1.is_a?(Hash) && hsh2.is_a?(Hash)

  hsh1.merge!(hsh2) { |_, v1, v2| deep_merge!(v1, v2) }
end
hab(*command) click to toggle source
Copyright

Chef Software, Inc.

License

Apache License, Version 2.0

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and li

# File lib/chef/resource/habitat/_habitat_shared.rb, line 17
def hab(*command)
  # Windows shell_out does not support arrays, so manually cleaning and joining
  hab_cmd = if windows?
              (["hab"] + command).flatten.compact.join(" ")
            else
              (["hab"] + command)
            end
  shell_out!(hab_cmd)
rescue Errno::ENOENT
  Chef::Log.fatal("'hab' binary not found, use the 'habitat_install' resource to install it first")
  raise
end
id_property() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 126
def id_property
  current_resource.class.identity_attr
end
json_to_property(match_instruction, property, resource_data) click to toggle source

Map part of a JSON (Hash) to resource property via JMESPath or user-supplied function

# File lib/chef/resource/_rest_resource.rb, line 146
def json_to_property(match_instruction, property, resource_data)
  case match_instruction
  when String
    JMESPath.search(match_instruction, resource_data)
  when Symbol
    function = "#{property}_from_json".to_sym
    raise "#{new_resource.name} missing #{function} method" unless self.class.protected_method_defined?(function)

    send(function, resource_data) || {}
  else
    raise TypeError, "Did not expect match type #{match_instruction.class}"
  end
end
load_current_resource() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 26
def load_current_resource
  @current_resource = new_resource.class.new(new_resource.name)

  required_properties.each do |name|
    requested = new_resource.send(name)
    current_resource.send(name, requested)
  end

  return @current_resource if rest_get_all.data.empty?

  resource_data = rest_get.data rescue nil
  return @current_resource if resource_data.nil? || resource_data.empty?

  @resource_exists = true

  # Map JSON contents to defined properties
  current_resource.class.rest_property_map.each do |property, match_instruction|
    property_value = json_to_property(match_instruction, property, resource_data)
    current_resource.send(property, property_value) unless property_value.nil?
  end

  current_resource
end
path_based_selection?() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 346
def path_based_selection?
  !query_based_selection?
end
property_map() click to toggle source

Map properties to their current values

# File lib/chef/resource/_rest_resource.rb, line 131
def property_map
  map = {}

  current_resource.class.state_properties.each do |property|
    name = property.options[:name]

    map[name] = current_resource.send(name)
  end

  map[id_property] = current_resource.send(id_property)

  map
end
property_to_json(property, match_instruction) click to toggle source

Map resource contents into a JSON (Hash) via JMESPath-like syntax or user-supplied function

# File lib/chef/resource/_rest_resource.rb, line 161
def property_to_json(property, match_instruction)
  case match_instruction
  when String
    bury(match_instruction, changed_value(property))
  when Symbol
    function = "#{property}_to_json".to_sym
    raise "#{new_resource.name} missing #{function} method" unless self.class.protected_method_defined?(function)

    value = new_resource.send(property)
    changed_value(property).nil? ? {} : send(function, value)
  else
    raise TypeError, "Did not expect match type #{match_instruction.class}"
  end
end
query_based_selection?() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 336
def query_based_selection?
  template_url = current_resource.class.rest_api_document

  # Will throw exception on presence of RFC 6570 templates
  URI.parse(template_url)
  true
rescue URI::InvalidURIError => _e
  false
end
required_properties() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 110
def required_properties
  current_resource.class.properties.select { |_, v| v.required? }.except(:name).keys
end
resource_exists?() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 106
def resource_exists?
  @resource_exists
end
rest_arity() click to toggle source

Return number of parameters needed to identify a resource (pre- and post-creation)

# File lib/chef/resource/_rest_resource.rb, line 271
def rest_arity
  rest_identity_map.keys.count
end
rest_delete() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 260
def rest_delete
  response = api_connection.delete(rest_url_document)

  rest_postprocess(response)
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_errorhandler(error_obj) click to toggle source

Override this for error handling of device-specifics (readable error messages)

# File lib/chef/resource/_rest_resource.rb, line 100
def rest_errorhandler(error_obj)
  error_obj
end
rest_get() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 219
def rest_get
  response = api_connection.get(rest_url_document)

  response = rest_postprocess(response)

  first_only = current_resource.class.rest_api_document_first_element_only
  response.data = response.data.first if first_only && response.data.is_a?(Array)

  response
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_get_all() click to toggle source

Generic REST helpers

# File lib/chef/resource/_rest_resource.rb, line 211
def rest_get_all
  response = api_connection.get(rest_url_collection)

  rest_postprocess(response)
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_identity_explicit() click to toggle source

Accept direct mapping like { “svm.name” => :name } for specifying the x-ary identity of a resource

# File lib/chef/resource/_rest_resource.rb, line 292
def rest_identity_explicit
  current_resource.class.rest_identity_map
end
rest_identity_implicit() click to toggle source

Parse document URL for RFC 6570 templates and map them to resource properties.

Examples:

Query based: "/api/protocols/san/igroups?name={name}&svm.name={svm}": { "name" => :name, "svm.name" => :svm }
Path based:  "/api/v1/{address}": { "address" => :address }
# File lib/chef/resource/_rest_resource.rb, line 302
def rest_identity_implicit
  template_url = current_resource.class.rest_api_document

  rfc_template = ::Addressable::Template.new(template_url)
  rfc_template_vars = rfc_template.variables

  # Shortcut for 0-ary resources
  return {} if rfc_template_vars.empty?

  if query_based_selection?
    uri_query = URI.parse(template_url).query

    if CGI.parse(uri_query).values.any?(&:empty?)
      raise "Need explicit identity mapping, as URL does not contain query parameters for all templates"
    end

    path_variables = CGI.parse(uri_query).keys
  elsif path_based_selection?
    path_variables = rfc_template_vars
  else
    # There is also
    raise "Unknown type of resource selection. Document URL does not seem to be path- or query-based?"
  end

  identity_map = {}
  path_variables.each_with_index do |v, i|
    next if rfc_template_vars[i].nil? # Not mapped to property, assume metaparameter

    identity_map[v] = rfc_template_vars[i].to_sym
  end

  identity_map
end
rest_identity_map() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 287
def rest_identity_map
  rest_identity_explicit || rest_identity_implicit
end
rest_identity_values() click to toggle source

Return mapping of template placeholders to property value of identity parameters

# File lib/chef/resource/_rest_resource.rb, line 276
def rest_identity_values
  data = {}

  rest_identity_map.each do |rfc_template, property|
    property_value = new_resource.send(property)
    data.merge! bury(rfc_template, property_value)
  end

  data
end
rest_patch(data) click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 252
def rest_patch(data)
  response = api_connection.patch(rest_url_document, data: data)

  rest_postprocess(response)
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_post(data) click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 232
def rest_post(data)
  data.merge! rest_identity_values

  response = api_connection.post(rest_url_collection, data: data)

  rest_postprocess(response)
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_postprocess(response) click to toggle source

Override this for postprocessing device-specifics (paging, data conversion)

# File lib/chef/resource/_rest_resource.rb, line 95
def rest_postprocess(response)
  response
end
rest_put(data) click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 242
def rest_put(data)
  data.merge! rest_identity_values

  response = api_connection.put(rest_url_collection, data: data)

  rest_postprocess(response)
rescue RestClient::Exception => e
  rest_errorhandler(e)
end
rest_url_collection() click to toggle source
# File lib/chef/resource/_rest_resource.rb, line 176
def rest_url_collection
  current_resource.class.rest_api_collection
end
rest_url_document() click to toggle source

Resource document URL after RFC 6570 template evaluation via properties substitution

# File lib/chef/resource/_rest_resource.rb, line 181
def rest_url_document
  template = ::Addressable::Template.new(current_resource.class.rest_api_document)
  template.expand(property_map).to_s
end
weekday_in_crontab(day) click to toggle source

Convert weekday input value into crontab format that could be written in the crontab @return [Integer, String] A weekday formed as per the user inputs.

# File lib/chef/resource/cron/_cron_shared.rb, line 95
def weekday_in_crontab(day)
  weekday = day.to_s.downcase.to_sym
  WEEKDAYS[weekday] || day
end