lib/config_curator/unit.rb
require 'logger'
require 'socket'
module ConfigCurator
# A unit is the base class for a type of configuration
# that should be installed.
# All units must specify a {#source} and a {#destination}.
class Unit
include Utils
# Error if the unit will fail to install.
class InstallFailed < RuntimeError; end
attr_accessor :logger, :source, :destination, :hosts, :packages
# Default {#options}.
DEFAULT_OPTIONS = {
# Unit installed relative to this path.
root: Dir.home,
# Automatically uninstall units that would not be installed.
uninstall: false,
# Package tool to use. See #package_lookup.
package_tool: nil
}
def initialize(options: {}, logger: nil)
self.options options
self.logger = logger unless logger.nil?
end
# Uses {DEFAULT_OPTIONS} as initial value.
# @param options [Hash] merged with current options
# @return [Hash] current options
def options(options = {})
@options ||= DEFAULT_OPTIONS
@options = @options.merge options
end
# Logger instance to use.
# @return [Logger] logger instance
def logger
@logger ||= Logger.new($stdout).tap do |log|
log.progname = self.class.name
end
end
# Full path to source.
# @return [String] expanded path to source
def source_path
File.expand_path source unless source.nil?
end
# Full path to destination.
# @return [String] expanded path to destination
def destination_path
File.expand_path File.join(options[:root], destination) unless destination.nil?
end
# Unit will be installed on these hosts.
# If empty, installed on any host.
# @return [Array] list of hostnames
def hosts
@hosts ||= []
end
# Unit installed only if listed packages are installed.
# @return [Array] list of package names
def packages
@packages ||= []
end
# A {PackageLookup} object for this unit.
def package_lookup
@package_lookup ||= PackageLookup.new tool: options[:package_tool]
end
# Uninstalls the unit.
# @return [Boolean] if the unit was uninstalled
def uninstall(force: false)
return true if uninstall? || force
false
end
# Checks if the unit should be uninstalled.
# @return [Boolean] if the unit should be uninstalled
def uninstall?
return true if !install? && options[:uninstall]
false
end
# Installs the unit.
# @return [Boolean] if the unit was installed
def install
return false unless install?
true
end
# Checks if the unit should be installed.
# @return [Boolean] if the unit should be installed
def install?
return false unless allowed_host?
return false unless packages_installed?
true
end
# Checks if the unit should be installed on this host.
# @return [Boolean] if the hostname is in {#hosts}
def allowed_host?
return true if hosts.empty?
hosts.include? hostname
end
# Checks if the packages required for this unit are installed.
# @return [Boolean] if the packages in {#packages} are installed
def packages_installed?
packages.map(&method(:pkg_exists?)).delete_if { |e| e }.empty?
end
private
# @return [String] the machine hostname
def hostname
Socket.gethostname
end
# @return [Boolean] if the package exists on the system
def pkg_exists?(pkg)
package_lookup.installed? pkg
end
end
end