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
@return [LzayHash] a configuration hash @api private
@return [Hash] a hash of the detected deprecated config attributes @api private
@return [Kitchen::Instance] the associated instance
@return [Hash] user provided configuration hash @api private
Public Class Methods
# File lib/kitchen/configurable.rb, line 29 def self.included(base) base.extend(ClassMethods) end
Public Instance Methods
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
@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
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):
-
/a/b/server/roles
-
/a/b/roles
-
$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
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
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
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
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
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
@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
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
@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
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
@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
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
# 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
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
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
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
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
@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
# File lib/kitchen/configurable.rb, line 388 def proxy_config_setting_present?(protocol) config.key?(protocol) && !config[protocol].nil? && !config[protocol].empty? end
# File lib/kitchen/configurable.rb, line 376 def proxy_from_config? proxy_setting_keys.any? do |protocol| !config[protocol].nil? end end
# 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
# File lib/kitchen/configurable.rb, line 361 def proxy_setting_keys %i{http_proxy https_proxy ftp_proxy no_proxy} end
@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
# 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
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
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
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
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