module Kitchen::Configurable

A mixin for providing configuration-related behavior such as default config (static, computed, inherited), required config, local path expansion, etc.

@author Fletcher Nichol <fnichol@nichol.ca>

Attributes

config[R]

@return [LzayHash] a configuration hash @api private

deprecated_config[R]

@return [Hash] a hash of the detected deprecated config attributes @api private

instance[R]

@return [Kitchen::Instance] the associated instance

provided_config[R]

@return [Hash] user provided configuration hash @api private

Public Class Methods

included(base) click to toggle source
# File lib/kitchen/configurable.rb, line 29
def self.included(base)
  base.extend(ClassMethods)
end

Public Instance Methods

[](attr) click to toggle source

Provides hash-like access to configuration keys.

@param attr [Object] configuration key @return [Object] value at configuration key

# File lib/kitchen/configurable.rb, line 61
def [](attr)
  config[attr]
end
bourne_shell?() click to toggle source

@return [TrueClass,FalseClass] true if `:shell_type` is `“bourne”` (or

unset, for backwards compatability)
# File lib/kitchen/configurable.rb, line 67
def bourne_shell?
  ["bourne", nil].include?(instance.platform.shell_type)
end
calculate_path(path, opts = {}) click to toggle source

Find an appropriate path to a file or directory, based on graceful fallback rules or returns nil if path cannot be determined.

Given an instance with suite named `“server”`, a `test_base_path` of `“/a/b”`, and a path segement of `“roles”` then following will be tried in order (first match that exists wins):

  1. /a/b/server/roles

  2. /a/b/roles

  3. $PWD/roles

@param path [String] the base path segment to search for @param opts [Hash] options @option opts [Symbol] :type either `:file` or `:directory` (default) @option opts [Symbol] :base_path a custom base path to search under,

default uses value from `config[:test_base_path]`

@return [String] path to the existing file or directory, or nil if file

or directory was not found

@raise [UserError] if `config` is used and is not set

# File lib/kitchen/configurable.rb, line 90
def calculate_path(path, opts = {})
  type = opts.fetch(:type, :directory)
  base = opts.fetch(:base_path) do
    config.fetch(:test_base_path) do |key|
      raise UserError, "#{key} is not found in #{self}"
    end
  end

  [
    File.join(base, instance.suite.name, path),
    File.join(base, path),
    File.join(Dir.pwd, path),
  ].find do |candidate|
    type == :directory ? File.directory?(candidate) : File.file?(candidate)
  end
end
config_keys() click to toggle source

Returns an array of configuration keys.

@return [Array] array of configuration keys

# File lib/kitchen/configurable.rb, line 110
def config_keys
  config.keys
end
diagnose() click to toggle source

Returns a Hash of configuration and other useful diagnostic information.

@return [Hash] a diagnostic hash

# File lib/kitchen/configurable.rb, line 117
def diagnose
  result = {}
  config_keys.sort.each { |k| result[k] = config[k] }
  result
end
diagnose_plugin() click to toggle source

Returns a Hash of configuration and other useful diagnostic information associated with the plugin itself (such as loaded version, class name, etc.).

@return [Hash] a diagnostic hash

# File lib/kitchen/configurable.rb, line 128
def diagnose_plugin
  result = {}
  result[:name] = name
  result.merge!(self.class.diagnose)
  result
end
finalize_config!(instance) click to toggle source

A lifecycle method that should be invoked when the object is about ready to be used. A reference to an Instance is required as configuration dependant data may be access through an Instance. This also acts as a hook point where the object may wish to perform other last minute checks, validations, or configuration expansions.

@param instance [Instance] an associated instance @return [self] itself, for use in chaining @raise [ClientError] if instance parameter is nil

# File lib/kitchen/configurable.rb, line 45
def finalize_config!(instance)
  raise ClientError, "Instance must be provided to #{self}" if instance.nil?

  @instance = instance
  expand_paths!
  deprecate_config!
  validate_config!
  load_needed_dependencies!

  self
end
name() click to toggle source

Returns the name of this plugin, suitable for display in a CLI.

@return [String] name of this plugin

# File lib/kitchen/configurable.rb, line 138
def name
  self.class.name.split("::").last
end
powershell_shell?() click to toggle source

@return [TrueClass,FalseClass] true if `:shell_type` is `“powershell”`

# File lib/kitchen/configurable.rb, line 143
def powershell_shell?
  ["powershell"].include?(instance.platform.shell_type)
end
remote_path_join(*parts) click to toggle source

Builds a file path based on the `:os_type` (`“windows”` or `“unix”`).

@return [String] joined path for instance's os_type

# File lib/kitchen/configurable.rb, line 150
def remote_path_join(*parts)
  path = File.join(*parts)
  windows_os? ? path.tr("/", "\\") : path.tr("\\", "/")
end
unix_os?() click to toggle source

@return [TrueClass,FalseClass] true if `:os_type` is `“unix”` (or

unset, for backwards compatibility)
# File lib/kitchen/configurable.rb, line 157
def unix_os?
  ["unix", nil].include?(instance.platform.os_type)
end
verify_dependencies() click to toggle source

Performs whatever tests that may be required to ensure that this plugin will be able to function in the current environment. This may involve checking for the presence of certain directories, software installed, etc.

@raise [UserError] if the plugin will not be able to perform or if a

documented dependency is missing from the system
# File lib/kitchen/configurable.rb, line 168
def verify_dependencies
  # this method may be left unimplemented if that is applicable
end
windows_os?() click to toggle source

@return [TrueClass,FalseClass] true if `:os_type` is `“windows”`

# File lib/kitchen/configurable.rb, line 173
def windows_os?
  ["windows"].include?(instance.platform.os_type)
end

Private Instance Methods

deprecate_config!() click to toggle source

Initialize detected deprecated configuration hash. Display warning if deprecations have been detected.

@api private

# File lib/kitchen/configurable.rb, line 229
    def deprecate_config!
      # We only want to display the deprecation list of config values once per execution on the default config.
      # This prevents the output of deprecations from being printed for each permutation of test suites.
      return if defined? @@has_been_warned_of_deprecations

      deprecated_attributes = LazyHash.new(self.class.deprecated_attributes, self)
      # Remove items from hash when not provided in the loaded config or when the rendered message is nil
      @deprecated_config = deprecated_attributes.delete_if { |attr, obj| !provided_config.key?(attr) || obj.nil? }

      unless deprecated_config.empty?
        warning = Util.outdent!(<<-MSG)
          Deprecated configuration detected:
          #{deprecated_config.keys.join("\n")}
          Run 'kitchen doctor' for details.
        MSG
        Error.warn_on_stderr(warning)

        # Set global var that the deprecation message has been printed
        @@has_been_warned_of_deprecations = true
      end
    end
env_wrapped(code) click to toggle source
# File lib/kitchen/configurable.rb, line 347
def env_wrapped(code)
  code_parts = resolve_proxy_settings_from_config
  code_parts << shell_env_var("TEST_KITCHEN", 1)
  code_parts << shell_env_var("CI", ENV["CI"]) if ENV["CI"]
  code_parts << shell_env_var("CHEF_LICENSE", ENV["CHEF_LICENSE"]) if ENV["CHEF_LICENSE"]
  ENV.select { |key, value| key.start_with?("TKENV_") }.each do |key, value|
    env_var_name = "#{key}".sub!("TKENV_", "")
    code_parts << shell_env_var(env_var_name, value)
  end

  code_parts << code
  code_parts.join("\n")
end
expand_paths!() click to toggle source

Expands file paths for certain configuration values. A configuration value is marked for file expansion with a expand_path_for declaration in the included class.

@api private

# File lib/kitchen/configurable.rb, line 210
def expand_paths!
  root_path = config[:kitchen_root] || Dir.pwd
  expanded_paths = LazyHash.new(self.class.expanded_paths, self).to_hash

  expanded_paths.each do |key, should_expand|
    next if !should_expand || config[key].nil? || config[key] == false

    config[key] = if config[key].is_a?(Array)
                    config[key].map { |path| File.expand_path(path, root_path) }
                  else
                    File.expand_path(config[key], root_path)
                  end
  end
end
export_proxy(env, type) click to toggle source

Helper method to export

@param env [Array] the environment to modify @param code [String] the type of proxy to export, one of 'http', 'https' or 'ftp' @api private

# File lib/kitchen/configurable.rb, line 397
def export_proxy(env, type)
  env << shell_env_var(type.to_s, ENV[type.to_s]) if ENV[type.to_s]
  env << shell_env_var(type.upcase.to_s, ENV[type.upcase.to_s]) if
    ENV[type.upcase.to_s]
end
init_config(config) click to toggle source

Initializes an internal configuration hash. The hash may contain callable blocks as values that are meant to be called lazily. This method is intended to be included in an object's .initialize method.

@param config [Hash] initial provided configuration @api private

# File lib/kitchen/configurable.rb, line 197
def init_config(config)
  @config = LazyHash.new(config, self)
  @provided_config = config.dup
  self.class.defaults.each do |attr, value|
    @config[attr] = value unless @config.key?(attr)
  end
end
load_needed_dependencies!() click to toggle source

Loads any required third party Ruby libraries or runs any shell out commands to prepare the plugin. This method will be called in the context of the main thread of execution and so does not necessarily have to be thread safe.

Note: any subclasses overriding this method would be well advised to call super when overriding this method, for example:

@example overriding `#load_needed_dependencies!`

class MyProvisioner < Kitchen::Provisioner::Base
  def load_needed_dependencies!
    super
    # any further work
  end
end

@raise [ClientError] if any library loading fails or any of the

dependency requirements cannot be satisfied

@api private

# File lib/kitchen/configurable.rb, line 271
def load_needed_dependencies!
  # this method may be left unimplemented if that is applicable
end
logger() click to toggle source

@return [Logger] the instance's logger or Test Kitchen's common logger

otherwise

@api private

# File lib/kitchen/configurable.rb, line 278
def logger
  instance ? instance.logger : Kitchen.logger
end
proxy_config_setting_present?(protocol) click to toggle source
# File lib/kitchen/configurable.rb, line 388
def proxy_config_setting_present?(protocol)
  config.key?(protocol) && !config[protocol].nil? && !config[protocol].empty?
end
proxy_from_config?() click to toggle source
# File lib/kitchen/configurable.rb, line 376
def proxy_from_config?
  proxy_setting_keys.any? do |protocol|
    !config[protocol].nil?
  end
end
proxy_from_environment?() click to toggle source
# File lib/kitchen/configurable.rb, line 382
def proxy_from_environment?
  proxy_setting_keys.any? do |protocol|
    !ENV[protocol.downcase.to_s].nil? || !ENV[protocol.upcase.to_s].nil?
  end
end
proxy_setting_keys() click to toggle source
# File lib/kitchen/configurable.rb, line 361
def proxy_setting_keys
  %i{http_proxy https_proxy ftp_proxy no_proxy}
end
reload_ps1_path() click to toggle source

@return [String] a powershell command to reload the `PATH` environment

variable, only to be used to support old Omnibus Chef packages that
require `PATH` to find the `ruby.exe` binary

@api private

# File lib/kitchen/configurable.rb, line 286
def reload_ps1_path
  [
    "$env:PATH = try {",
    "[System.Environment]::GetEnvironmentVariable('PATH','Machine')",
    "} catch { $env:PATH }\n\n",
  ].join("\n")
end
resolve_proxy_settings_from_config() click to toggle source
# File lib/kitchen/configurable.rb, line 365
def resolve_proxy_settings_from_config
  proxy_setting_keys.each_with_object([]) do |protocol, set_env|
    if !config.key?(protocol) || config[protocol].nil?
      export_proxy(set_env, protocol)
    elsif proxy_config_setting_present?(protocol)
      set_env << shell_env_var(protocol.downcase.to_s, config[protocol])
      set_env << shell_env_var(protocol.upcase.to_s, config[protocol])
    end
  end
end
shell_env_var(name, value) click to toggle source

Builds a shell environment variable assignment string for the required shell type.

@param name [String] variable name @param value [String] variable value @return [String] shell variable assignment @api private

# File lib/kitchen/configurable.rb, line 301
def shell_env_var(name, value)
  if powershell_shell?
    shell_var("env:#{name}", value)
  else
    "#{shell_var(name, value)}; export #{name}"
  end
end
shell_var(name, value) click to toggle source

Builds a shell variable assignment string for the required shell type.

@param name [String] variable name @param value [String] variable value @return [String] shell variable assignment @api private

# File lib/kitchen/configurable.rb, line 315
def shell_var(name, value)
  if powershell_shell?
    %{$#{name} = "#{value}"}
  else
    %{#{name}="#{value}"}
  end
end
validate_config!() click to toggle source

Runs all validations set up for the included class. Each validation is for a specific configuration attribute and has an associated callable block. Each validation block is called with the attribute, its value, and the included object for context.

@api private

# File lib/kitchen/configurable.rb, line 329
def validate_config!
  self.class.validations.each do |attr, block|
    block.call(attr, config[attr], self)
  end
end
wrap_shell_code(code) click to toggle source

Wraps a body of shell code with common context appropriate for the type of shell.

@param code [String] the shell code to be wrapped @return [String] wrapped shell code @api private

# File lib/kitchen/configurable.rb, line 341
def wrap_shell_code(code)
  return env_wrapped(code) if powershell_shell?

  Util.wrap_command((env_wrapped code))
end