fuel-library/deployment/puppet/merge_yaml/lib/puppet/provider/merge_yaml_settings/ruby.rb

183 lines
5.7 KiB
Ruby

require 'yaml'
require_relative '../../yaml_deep_merge'
Puppet::Type.type(:merge_yaml_settings).provide(:ruby) do
desc 'Support for merging yaml configuration files.'
attr_reader :resource
# Create a new target YAML file
def create
debug 'Call: create'
data = merged_data
return if data.empty?
write_to_file(resource[:path], data)
end
# Convert data structure to a YAML file contents
# @param [Hash] data
# @return [String]
def serialize_data(data)
data.to_yaml.gsub('x5c', '\\')
end
# A hash of options for the deep merge module
# @return [Hash]
def deep_merge_options
{
:preserve_unmergeables => resource[:preserve_unmergeables],
:knockout_prefix => resource[:knockout_prefix],
:overwrite_arrays => resource[:overwrite_arrays],
:sort_merged_arrays => resource[:sort_merged_arrays],
:unpack_arrays => resource[:unpack_arrays],
:merge_hash_arrays => resource[:merge_hash_arrays],
:extend_existing_arrays => resource[:extend_existing_arrays],
:merge_debug => resource[:merge_debug],
}
end
# Enable additional debug messages
# @return [true,false]
def merge_debug
deep_merge_options[:merge_debug]
end
# Remove the target yaml file
def destroy
debug 'Call: destroy'
File.unlink resource[:path] if File.exists? resource[:path]
end
# Check if the sample file contains the correct merged structure
def exists?
debug 'Call: exists?'
return false unless target_yaml_file?
debug "Exists original: #{original_data.inspect}" if merge_debug
debug "Exists merged: #{merged_data.inspect}" if merge_debug
result = original_data == merged_data
debug "Return: #{result}"
result
end
# Purge the :undef values which have been introduced with Puppet4
def undef2nil(structure)
if structure.is_a? Array
structure.map do |element|
undef2nil element
end
elsif structure.is_a? Hash
hash = {}
structure.each do |key, value|
hash[key] = undef2nil value
end
hash
else
return nil if structure == :undef
structure
end
end
# Produce the merged data structure by merging
# the original data data with the override data.
# @return [Hash]
def merged_data
debug 'Call: merged_data'
debug "Merge original: #{original_data.inspect}" if merge_debug
debug "Merge override: #{override_data.inspect}" if merge_debug
original_data_clone = Marshal.load Marshal.dump original_data
YamlDeepMerge.deep_merge! override_data, original_data_clone, deep_merge_options
original_data_clone = undef2nil original_data_clone
debug "Result: #{original_data_clone.inspect}" if merge_debug
original_data_clone
end
# Write the merged data to the specified file name
# @param [String] file_name
# @param [Hash] data
def write_to_file(file_name, data)
debug "Writing content to the file: '#{file_name}'"
content = serialize_data data
begin
File.open(file_name, 'w') { |f| f.puts content }
rescue => exception
fail "The file: '#{file_name}' cannot be written! #{exception}"
end
end
# Read the contents of the YAML file
# @param [String] file_name
def read_from_file(file_name)
debug "Reading content from the file: '#{file_name}'"
begin
YAML.load_file(file_name)
rescue => exception
warn "The file: '#{file_name}' cannot be read! #{exception}"
nil
end
end
# The original portion of the YAML file.
# If the target file if present it will be loaded.
# If there is no target file, the original_data will be loaded as a file
# or as a data structure.
# @return [Hash,Array]
def original_data
return @original_data if @original_data
if target_yaml_file?
@original_data = read_from_file resource[:path]
return @original_data if @original_data
end
if original_data_file?
@original_data = read_from_file resource[:original_data]
return @original_data if @original_data
end
unless resource[:original_data].is_a? Hash or resource[:original_data].is_a? Array
fail "The original_data should be either a path to the YAML file or the data structure! Got: #{resource[:original_data]}"
end
@original_data = resource[:original_data]
end
# The override portion of the YAML file.
# If the override_data are provided as a path to a file
# the file will be loaded.
# @return [Hash,Array]
def override_data
return @override_data if @override_data
if override_data_file?
@override_data = read_from_file resource[:override_data]
return @override_data if @override_data
end
unless resource[:override_data].is_a? Hash or resource[:override_data].is_a? Array
fail "The override_data should be either a path to the YAML file or the data structure! Got: #{resource[:override_data]}"
end
@override_data = resource[:override_data]
end
# Check if the target YAML file exists
# @return [true,false]
def target_yaml_file?
return false unless resource[:path].is_a? String
return false unless File.absolute_path resource[:path]
File.file? resource[:path]
end
# Check if original_data are provided as a file
# and the file is present
# @return [true,false]
def original_data_file?
return false unless resource[:original_data].is_a? String
return false unless File.absolute_path resource[:original_data]
File.file? resource[:original_data]
end
# Check if the override_data are provided as a file
# and the file is present
# @return [true,false]
def override_data_file?
return false unless resource[:override_data].is_a? String
return false unless File.absolute_path resource[:override_data]
File.file? resource[:override_data]
end
end