From 543e5108d6b50928cac3c7e031e6a96d5f664ee1 Mon Sep 17 00:00:00 2001 From: varac Date: Sat, 11 Jun 2016 15:27:45 +0200 Subject: Move custom functions to site_config module --- .../parser/functions/create_resources_hash_from.rb | 116 ------ puppet/lib/puppet/parser/functions/sorted_json.rb | 47 --- puppet/lib/puppet/parser/functions/sorted_yaml.rb | 400 --------------------- .../parser/functions/create_resources_hash_from.rb | 116 ++++++ .../lib/puppet/parser/functions/sorted_json.rb | 47 +++ .../lib/puppet/parser/functions/sorted_yaml.rb | 400 +++++++++++++++++++++ 6 files changed, 563 insertions(+), 563 deletions(-) delete mode 100644 puppet/lib/puppet/parser/functions/create_resources_hash_from.rb delete mode 100644 puppet/lib/puppet/parser/functions/sorted_json.rb delete mode 100644 puppet/lib/puppet/parser/functions/sorted_yaml.rb create mode 100644 puppet/modules/site_config/lib/puppet/parser/functions/create_resources_hash_from.rb create mode 100644 puppet/modules/site_config/lib/puppet/parser/functions/sorted_json.rb create mode 100644 puppet/modules/site_config/lib/puppet/parser/functions/sorted_yaml.rb diff --git a/puppet/lib/puppet/parser/functions/create_resources_hash_from.rb b/puppet/lib/puppet/parser/functions/create_resources_hash_from.rb deleted file mode 100644 index 47d0df9c..00000000 --- a/puppet/lib/puppet/parser/functions/create_resources_hash_from.rb +++ /dev/null @@ -1,116 +0,0 @@ -# -# create_resources_hash_from.rb -# -# 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 -# limitations under the License. -# - -module Puppet::Parser::Functions - newfunction(:create_resources_hash_from, :type => :rvalue, :doc => <<-EOS -Given: - A formatted string (to use as the resource name) - An array to loop through (because puppet cannot loop) - A hash defining the parameters for a resource - And optionally an hash of parameter names to add to the resource and an - associated formatted string that should be configured with the current - element of the loop array - -This function will return a hash of hashes that can be used with the -create_resources function. - -*Examples:* - $allowed_hosts = ['10.0.0.0/8', '192.168.0.0/24'] - $resource_name = "100 allow %s to apache on ports 80" - $my_resource_hash = { - 'proto' => 'tcp', - 'action' => 'accept', - 'dport' => 80 - } - $dynamic_parameters = { - 'source' => '%s' - } - - $created_resource_hash = create_resources_hash_from($resource_name, $allowed_hosts, $my_resource_hash, $dynamic_parameters) - -$created_resource_hash would equal: - { - '100 allow 10.0.0.0/8 to apache on ports 80' => { - 'proto' => 'tcp', - 'action' => 'accept', - 'dport' => 80, - 'source' => '10.0.0.0/8' - }, - '100 allow 192.168.0.0/24 to apache on ports 80' => { - 'proto' => 'tcp', - 'action' => 'accept', - 'dport' => 80, - 'source' => '192.168.0.0/24' - } - } - -$created_resource_hash could then be used with create_resources - - create_resources(firewall, $created_resource_hash) - -To create a bunch of resources in a way that would only otherwise be possible -with a loop of some description. - EOS - ) do |arguments| - - raise Puppet::ParseError, "create_resources_hash_from(): Wrong number of arguments " + - "given (#{arguments.size} for 3 or 4)" if arguments.size < 3 or arguments.size > 4 - - formatted_string = arguments[0] - - unless formatted_string.is_a?(String) - raise(Puppet::ParseError, 'create_resources_hash_from(): first argument must be a string') - end - - loop_array = arguments[1] - - unless loop_array.is_a?(Array) - raise(Puppet::ParseError, 'create_resources_hash_from(): second argument must be an array') - end - - resource_hash = arguments[2] - unless resource_hash.is_a?(Hash) - raise(Puppet::ParseError, 'create_resources_hash_from(): third argument must be a hash') - end - - if arguments.size == 4 - dynamic_parameters = arguments[3] - unless dynamic_parameters.is_a?(Hash) - raise(Puppet::ParseError, 'create_resources_hash_from(): fourth argument must be a hash') - end - end - - result = {} - - loop_array.each do |i| - my_resource_hash = resource_hash.clone - if dynamic_parameters - dynamic_parameters.each do |param, value| - if my_resource_hash.member?(param) - raise(Puppet::ParseError, "create_resources_hash_from(): dynamic_parameter '#{param}' already exists in resource hash") - end - my_resource_hash[param] = sprintf(value,[i]) - end - end - result[sprintf(formatted_string,[i])] = my_resource_hash - end - - result - end -end - -# vim: set ts=2 sw=2 et : -# encoding: utf-8 diff --git a/puppet/lib/puppet/parser/functions/sorted_json.rb b/puppet/lib/puppet/parser/functions/sorted_json.rb deleted file mode 100644 index 605da00e..00000000 --- a/puppet/lib/puppet/parser/functions/sorted_json.rb +++ /dev/null @@ -1,47 +0,0 @@ -# -# Written by Gavin Mogan, from https://gist.github.com/halkeye/2287885 -# Put in the public domain by the author. -# - -require 'json' - -def sorted_json(obj) - case obj - when String, Fixnum, Float, TrueClass, FalseClass, NilClass - return obj.to_json - when Array - arrayRet = [] - obj.each do |a| - arrayRet.push(sorted_json(a)) - end - return "[" << arrayRet.join(',') << "]"; - when Hash - ret = [] - obj.keys.sort.each do |k| - ret.push(k.to_json << ":" << sorted_json(obj[k])) - end - return "{" << ret.join(",") << "}"; - else - raise Exception("Unable to handle object of type <%s>" % obj.class.to_s) - end -end - -module Puppet::Parser::Functions - newfunction(:sorted_json, :type => :rvalue, :doc => <<-EOS -This function takes data, outputs making sure the hash keys are sorted - -*Examples:* - - sorted_json({'key'=>'value'}) - -Would return: {'key':'value'} - EOS - ) do |arguments| - raise(Puppet::ParseError, "sorted_json(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size != 1 - - json = arguments[0] - return sorted_json(json) - end -end - diff --git a/puppet/lib/puppet/parser/functions/sorted_yaml.rb b/puppet/lib/puppet/parser/functions/sorted_yaml.rb deleted file mode 100644 index 46cd46ce..00000000 --- a/puppet/lib/puppet/parser/functions/sorted_yaml.rb +++ /dev/null @@ -1,400 +0,0 @@ -# encoding: UTF-8 -# -# provides sorted_yaml() function, using Ya2YAML. -# see https://github.com/afunai/ya2yaml -# - -class Ya2YAML - # - # Author:: Akira FUNAI - # Copyright:: Copyright (c) 2006-2010 Akira FUNAI - # License:: MIT License - # - - def initialize(opts = {}) - options = opts.dup - options[:indent_size] = 2 if options[:indent_size].to_i <= 0 - options[:minimum_block_length] = 0 if options[:minimum_block_length].to_i <= 0 - options.update( - { - :printable_with_syck => true, - :escape_b_specific => true, - :escape_as_utf8 => true, - } - ) if options[:syck_compatible] - - @options = options - end - - def _ya2yaml(obj) - #raise 'set $KCODE to "UTF8".' if (RUBY_VERSION < '1.9.0') && ($KCODE != 'UTF8') - if (RUBY_VERSION < '1.9.0') - $KCODE = 'UTF8' - end - '--- ' + emit(obj, 1) + "\n" - rescue SystemStackError - raise ArgumentError, "ya2yaml can't handle circular references" - end - - private - - def emit(obj, level) - case obj - when Array - if (obj.length == 0) - '[]' - else - indent = "\n" + s_indent(level - 1) - ### - ### NOTE: a minor modification to normal Ya2YAML... - ### We want arrays to be output in sorted order, not just - ### Hashes. - ### - #obj.collect {|o| - # indent + '- ' + emit(o, level + 1) - #}.join('') - obj.sort {|a,b| a.to_s <=> b.to_s}.collect {|o| - indent + '- ' + emit(o, level + 1) - }.join('') - end - when Hash - if (obj.length == 0) - '{}' - else - indent = "\n" + s_indent(level - 1) - hash_order = @options[:hash_order] - if (hash_order && level == 1) - hash_keys = obj.keys.sort {|x, y| - x_order = hash_order.index(x) ? hash_order.index(x) : Float::MAX - y_order = hash_order.index(y) ? hash_order.index(y) : Float::MAX - o = (x_order <=> y_order) - (o != 0) ? o : (x.to_s <=> y.to_s) - } - elsif @options[:preserve_order] - hash_keys = obj.keys - else - hash_keys = obj.keys.sort {|x, y| x.to_s <=> y.to_s } - end - hash_keys.collect {|k| - key = emit(k, level + 1) - if ( - is_one_plain_line?(key) || - key =~ /\A(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_NULL})\z/x - ) - indent + key + ': ' + emit(obj[k], level + 1) - else - indent + '? ' + key + - indent + ': ' + emit(obj[k], level + 1) - end - }.join('') - end - when NilClass - '~' - when String - emit_string(obj, level) - when TrueClass, FalseClass - obj.to_s - when Fixnum, Bignum, Float - obj.to_s - when Date - obj.to_s - when Time - offset = obj.gmtoff - off_hm = sprintf( - '%+.2d:%.2d', - (offset / 3600.0).to_i, - (offset % 3600.0) / 60 - ) - u_sec = (obj.usec != 0) ? sprintf(".%.6d", obj.usec) : '' - obj.strftime("%Y-%m-%d %H:%M:%S#{u_sec} #{off_hm}") - when Symbol - '!ruby/symbol ' + emit_string(obj.to_s, level) - when Range - '!ruby/range ' + obj.to_s - when Regexp - '!ruby/regexp ' + obj.inspect - else - case - when obj.is_a?(Struct) - struct_members = {} - obj.each_pair{|k, v| struct_members[k.to_s] = v } - '!ruby/struct:' + obj.class.to_s.sub(/^(Struct::(.+)|.*)$/, '\2') + ' ' + - emit(struct_members, level + 1) - else - # serialized as a generic object - object_members = {} - obj.instance_variables.each{|k, v| - object_members[k.to_s.sub(/^@/, '')] = obj.instance_variable_get(k) - } - '!ruby/object:' + obj.class.to_s + ' ' + - emit(object_members, level + 1) - end - end - end - - def emit_string(str, level) - (is_string, is_printable, is_one_line, is_one_plain_line) = string_type(str) - if is_string - if is_printable - if is_one_plain_line - emit_simple_string(str, level) - else - (is_one_line || str.length < @options[:minimum_block_length]) ? - emit_quoted_string(str, level) : - emit_block_string(str, level) - end - else - emit_quoted_string(str, level) - end - else - emit_base64_binary(str, level) - end - end - - def emit_simple_string(str, level) - str - end - - def emit_block_string(str, level) - str = normalize_line_break(str) - - indent = s_indent(level) - indentation_indicator = (str =~ /\A /) ? indent.size.to_s : '' - str =~ /(#{REX_NORMAL_LB}*)\z/ - chomping_indicator = case $1.length - when 0 - '-' - when 1 - '' - else - '+' - end - - str.chomp! - str.gsub!(/#{REX_NORMAL_LB}/) { - $1 + indent - } - '|' + indentation_indicator + chomping_indicator + "\n" + indent + str - end - - def emit_quoted_string(str, level) - str = yaml_escape(normalize_line_break(str)) - if (str.length < @options[:minimum_block_length]) - str.gsub!(/#{REX_NORMAL_LB}/) { ESCAPE_SEQ_LB[$1] } - else - str.gsub!(/#{REX_NORMAL_LB}$/) { ESCAPE_SEQ_LB[$1] } - str.gsub!(/(#{REX_NORMAL_LB}+)(.)/) { - trail_c = $3 - $1 + trail_c.sub(/([\t ])/) { ESCAPE_SEQ_WS[$1] } - } - indent = s_indent(level) - str.gsub!(/#{REX_NORMAL_LB}/) { - ESCAPE_SEQ_LB[$1] + "\\\n" + indent - } - end - '"' + str + '"' - end - - def emit_base64_binary(str, level) - indent = "\n" + s_indent(level) - base64 = [str].pack('m') - '!binary |' + indent + base64.gsub(/\n(?!\z)/, indent) - end - - def string_type(str) - if str.respond_to?(:encoding) && (!str.valid_encoding? || str.encoding == Encoding::ASCII_8BIT) - return false, false, false, false - end - (ucs_codes = str.unpack('U*')) rescue ( - # ArgumentError -> binary data - return false, false, false, false - ) - if ( - @options[:printable_with_syck] && - str =~ /\A#{REX_ANY_LB}* | #{REX_ANY_LB}*\z|#{REX_ANY_LB}{2}\z/ - ) - # detour Syck bug - return true, false, nil, false - end - ucs_codes.each {|ucs_code| - return true, false, nil, false unless is_printable?(ucs_code) - } - return true, true, is_one_line?(str), is_one_plain_line?(str) - end - - def is_printable?(ucs_code) - # YAML 1.1 / 4.1.1. - ( - [0x09, 0x0a, 0x0d, 0x85].include?(ucs_code) || - (ucs_code <= 0x7e && ucs_code >= 0x20) || - (ucs_code <= 0xd7ff && ucs_code >= 0xa0) || - (ucs_code <= 0xfffd && ucs_code >= 0xe000) || - (ucs_code <= 0x10ffff && ucs_code >= 0x10000) - ) && - !( - # treat LS/PS as non-printable characters - @options[:escape_b_specific] && - (ucs_code == 0x2028 || ucs_code == 0x2029) - ) - end - - def is_one_line?(str) - str !~ /#{REX_ANY_LB}(?!\z)/ - end - - def is_one_plain_line?(str) - # YAML 1.1 / 4.6.11. - str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/ && - str !~ /[:\#\s\[\]\{\},]/ && - str !~ /#{REX_ANY_LB}/ && - str !~ /^(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_MERGE} - |#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x - end - - def s_indent(level) - # YAML 1.1 / 4.2.2. - ' ' * (level * @options[:indent_size]) - end - - def normalize_line_break(str) - # YAML 1.1 / 4.1.4. - str.gsub(/(#{REX_CRLF}|#{REX_CR}|#{REX_NEL})/, "\n") - end - - def yaml_escape(str) - # YAML 1.1 / 4.1.6. - str.gsub(/[^a-zA-Z0-9]/u) {|c| - ucs_code, = (c.unpack('U') rescue [??]) - case - when ESCAPE_SEQ[c] - ESCAPE_SEQ[c] - when is_printable?(ucs_code) - c - when @options[:escape_as_utf8] - c.respond_to?(:bytes) ? - c.bytes.collect {|b| '\\x%.2x' % b }.join : - '\\x' + c.unpack('H2' * c.size).join('\\x') - when ucs_code == 0x2028 || ucs_code == 0x2029 - ESCAPE_SEQ_LB[c] - when ucs_code <= 0x7f - sprintf('\\x%.2x', ucs_code) - when ucs_code <= 0xffff - sprintf('\\u%.4x', ucs_code) - else - sprintf('\\U%.8x', ucs_code) - end - } - end - - module Constants - UCS_0X85 = [0x85].pack('U') # c285@UTF8 Unicode next line - UCS_0XA0 = [0xa0].pack('U') # c2a0@UTF8 Unicode non-breaking space - UCS_0X2028 = [0x2028].pack('U') # e280a8@UTF8 Unicode line separator - UCS_0X2029 = [0x2029].pack('U') # e280a9@UTF8 Unicode paragraph separator - - # non-break characters - ESCAPE_SEQ = { - "\x00" => '\\0', - "\x07" => '\\a', - "\x08" => '\\b', - "\x0b" => '\\v', - "\x0c" => '\\f', - "\x1b" => '\\e', - "\"" => '\\"', - "\\" => '\\\\', - } - - # non-breaking space - ESCAPE_SEQ_NS = { - UCS_0XA0 => '\\_', - } - - # white spaces - ESCAPE_SEQ_WS = { - "\x09" => '\\t', - " " => '\\x20', - } - - # line breaks - ESCAPE_SEQ_LB ={ - "\x0a" => '\\n', - "\x0d" => '\\r', - UCS_0X85 => '\\N', - UCS_0X2028 => '\\L', - UCS_0X2029 => '\\P', - } - - # regexps for line breaks - REX_LF = Regexp.escape("\x0a") - REX_CR = Regexp.escape("\x0d") - REX_CRLF = Regexp.escape("\x0d\x0a") - REX_NEL = Regexp.escape(UCS_0X85) - REX_LS = Regexp.escape(UCS_0X2028) - REX_PS = Regexp.escape(UCS_0X2029) - - REX_ANY_LB = /(#{REX_LF}|#{REX_CR}|#{REX_NEL}|#{REX_LS}|#{REX_PS})/ - REX_NORMAL_LB = /(#{REX_LF}|#{REX_LS}|#{REX_PS})/ - - # regexps for language-Independent types for YAML1.1 - REX_BOOL = / - y|Y|yes|Yes|YES|n|N|no|No|NO - |true|True|TRUE|false|False|FALSE - |on|On|ON|off|Off|OFF - /x - REX_FLOAT = / - [-+]?([0-9][0-9_]*)?\.[0-9.]*([eE][-+][0-9]+)? # (base 10) - |[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]* # (base 60) - |[-+]?\.(inf|Inf|INF) # (infinity) - |\.(nan|NaN|NAN) # (not a number) - /x - REX_INT = / - [-+]?0b[0-1_]+ # (base 2) - |[-+]?0[0-7_]+ # (base 8) - |[-+]?(0|[1-9][0-9_]*) # (base 10) - |[-+]?0x[0-9a-fA-F_]+ # (base 16) - |[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60) - /x - REX_MERGE = / - << - /x - REX_NULL = / - ~ # (canonical) - |null|Null|NULL # (English) - | # (Empty) - /x - REX_TIMESTAMP = / - [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] # (ymd) - |[0-9][0-9][0-9][0-9] # (year) - -[0-9][0-9]? # (month) - -[0-9][0-9]? # (day) - ([Tt]|[ \t]+)[0-9][0-9]? # (hour) - :[0-9][0-9] # (minute) - :[0-9][0-9] # (second) - (\.[0-9]*)? # (fraction) - (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? # (time zone) - /x - REX_VALUE = / - = - /x - end - - include Constants -end - -module Puppet::Parser::Functions - newfunction(:sorted_yaml, - :type => :rvalue, - :doc => "This function outputs yaml, but ensures the keys are sorted." - ) do |arguments| - - if arguments.is_a?(Array) - if arguments.size != 1 - raise(Puppet::ParseError, "sorted_yaml(): Wrong number of arguments given (#{arguments.size} for 1)") - end - yaml = arguments.first - else - yaml = arguments - end - return Ya2YAML.new()._ya2yaml(yaml) - end -end diff --git a/puppet/modules/site_config/lib/puppet/parser/functions/create_resources_hash_from.rb b/puppet/modules/site_config/lib/puppet/parser/functions/create_resources_hash_from.rb new file mode 100644 index 00000000..47d0df9c --- /dev/null +++ b/puppet/modules/site_config/lib/puppet/parser/functions/create_resources_hash_from.rb @@ -0,0 +1,116 @@ +# +# create_resources_hash_from.rb +# +# 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 +# limitations under the License. +# + +module Puppet::Parser::Functions + newfunction(:create_resources_hash_from, :type => :rvalue, :doc => <<-EOS +Given: + A formatted string (to use as the resource name) + An array to loop through (because puppet cannot loop) + A hash defining the parameters for a resource + And optionally an hash of parameter names to add to the resource and an + associated formatted string that should be configured with the current + element of the loop array + +This function will return a hash of hashes that can be used with the +create_resources function. + +*Examples:* + $allowed_hosts = ['10.0.0.0/8', '192.168.0.0/24'] + $resource_name = "100 allow %s to apache on ports 80" + $my_resource_hash = { + 'proto' => 'tcp', + 'action' => 'accept', + 'dport' => 80 + } + $dynamic_parameters = { + 'source' => '%s' + } + + $created_resource_hash = create_resources_hash_from($resource_name, $allowed_hosts, $my_resource_hash, $dynamic_parameters) + +$created_resource_hash would equal: + { + '100 allow 10.0.0.0/8 to apache on ports 80' => { + 'proto' => 'tcp', + 'action' => 'accept', + 'dport' => 80, + 'source' => '10.0.0.0/8' + }, + '100 allow 192.168.0.0/24 to apache on ports 80' => { + 'proto' => 'tcp', + 'action' => 'accept', + 'dport' => 80, + 'source' => '192.168.0.0/24' + } + } + +$created_resource_hash could then be used with create_resources + + create_resources(firewall, $created_resource_hash) + +To create a bunch of resources in a way that would only otherwise be possible +with a loop of some description. + EOS + ) do |arguments| + + raise Puppet::ParseError, "create_resources_hash_from(): Wrong number of arguments " + + "given (#{arguments.size} for 3 or 4)" if arguments.size < 3 or arguments.size > 4 + + formatted_string = arguments[0] + + unless formatted_string.is_a?(String) + raise(Puppet::ParseError, 'create_resources_hash_from(): first argument must be a string') + end + + loop_array = arguments[1] + + unless loop_array.is_a?(Array) + raise(Puppet::ParseError, 'create_resources_hash_from(): second argument must be an array') + end + + resource_hash = arguments[2] + unless resource_hash.is_a?(Hash) + raise(Puppet::ParseError, 'create_resources_hash_from(): third argument must be a hash') + end + + if arguments.size == 4 + dynamic_parameters = arguments[3] + unless dynamic_parameters.is_a?(Hash) + raise(Puppet::ParseError, 'create_resources_hash_from(): fourth argument must be a hash') + end + end + + result = {} + + loop_array.each do |i| + my_resource_hash = resource_hash.clone + if dynamic_parameters + dynamic_parameters.each do |param, value| + if my_resource_hash.member?(param) + raise(Puppet::ParseError, "create_resources_hash_from(): dynamic_parameter '#{param}' already exists in resource hash") + end + my_resource_hash[param] = sprintf(value,[i]) + end + end + result[sprintf(formatted_string,[i])] = my_resource_hash + end + + result + end +end + +# vim: set ts=2 sw=2 et : +# encoding: utf-8 diff --git a/puppet/modules/site_config/lib/puppet/parser/functions/sorted_json.rb b/puppet/modules/site_config/lib/puppet/parser/functions/sorted_json.rb new file mode 100644 index 00000000..605da00e --- /dev/null +++ b/puppet/modules/site_config/lib/puppet/parser/functions/sorted_json.rb @@ -0,0 +1,47 @@ +# +# Written by Gavin Mogan, from https://gist.github.com/halkeye/2287885 +# Put in the public domain by the author. +# + +require 'json' + +def sorted_json(obj) + case obj + when String, Fixnum, Float, TrueClass, FalseClass, NilClass + return obj.to_json + when Array + arrayRet = [] + obj.each do |a| + arrayRet.push(sorted_json(a)) + end + return "[" << arrayRet.join(',') << "]"; + when Hash + ret = [] + obj.keys.sort.each do |k| + ret.push(k.to_json << ":" << sorted_json(obj[k])) + end + return "{" << ret.join(",") << "}"; + else + raise Exception("Unable to handle object of type <%s>" % obj.class.to_s) + end +end + +module Puppet::Parser::Functions + newfunction(:sorted_json, :type => :rvalue, :doc => <<-EOS +This function takes data, outputs making sure the hash keys are sorted + +*Examples:* + + sorted_json({'key'=>'value'}) + +Would return: {'key':'value'} + EOS + ) do |arguments| + raise(Puppet::ParseError, "sorted_json(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + json = arguments[0] + return sorted_json(json) + end +end + diff --git a/puppet/modules/site_config/lib/puppet/parser/functions/sorted_yaml.rb b/puppet/modules/site_config/lib/puppet/parser/functions/sorted_yaml.rb new file mode 100644 index 00000000..46cd46ce --- /dev/null +++ b/puppet/modules/site_config/lib/puppet/parser/functions/sorted_yaml.rb @@ -0,0 +1,400 @@ +# encoding: UTF-8 +# +# provides sorted_yaml() function, using Ya2YAML. +# see https://github.com/afunai/ya2yaml +# + +class Ya2YAML + # + # Author:: Akira FUNAI + # Copyright:: Copyright (c) 2006-2010 Akira FUNAI + # License:: MIT License + # + + def initialize(opts = {}) + options = opts.dup + options[:indent_size] = 2 if options[:indent_size].to_i <= 0 + options[:minimum_block_length] = 0 if options[:minimum_block_length].to_i <= 0 + options.update( + { + :printable_with_syck => true, + :escape_b_specific => true, + :escape_as_utf8 => true, + } + ) if options[:syck_compatible] + + @options = options + end + + def _ya2yaml(obj) + #raise 'set $KCODE to "UTF8".' if (RUBY_VERSION < '1.9.0') && ($KCODE != 'UTF8') + if (RUBY_VERSION < '1.9.0') + $KCODE = 'UTF8' + end + '--- ' + emit(obj, 1) + "\n" + rescue SystemStackError + raise ArgumentError, "ya2yaml can't handle circular references" + end + + private + + def emit(obj, level) + case obj + when Array + if (obj.length == 0) + '[]' + else + indent = "\n" + s_indent(level - 1) + ### + ### NOTE: a minor modification to normal Ya2YAML... + ### We want arrays to be output in sorted order, not just + ### Hashes. + ### + #obj.collect {|o| + # indent + '- ' + emit(o, level + 1) + #}.join('') + obj.sort {|a,b| a.to_s <=> b.to_s}.collect {|o| + indent + '- ' + emit(o, level + 1) + }.join('') + end + when Hash + if (obj.length == 0) + '{}' + else + indent = "\n" + s_indent(level - 1) + hash_order = @options[:hash_order] + if (hash_order && level == 1) + hash_keys = obj.keys.sort {|x, y| + x_order = hash_order.index(x) ? hash_order.index(x) : Float::MAX + y_order = hash_order.index(y) ? hash_order.index(y) : Float::MAX + o = (x_order <=> y_order) + (o != 0) ? o : (x.to_s <=> y.to_s) + } + elsif @options[:preserve_order] + hash_keys = obj.keys + else + hash_keys = obj.keys.sort {|x, y| x.to_s <=> y.to_s } + end + hash_keys.collect {|k| + key = emit(k, level + 1) + if ( + is_one_plain_line?(key) || + key =~ /\A(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_NULL})\z/x + ) + indent + key + ': ' + emit(obj[k], level + 1) + else + indent + '? ' + key + + indent + ': ' + emit(obj[k], level + 1) + end + }.join('') + end + when NilClass + '~' + when String + emit_string(obj, level) + when TrueClass, FalseClass + obj.to_s + when Fixnum, Bignum, Float + obj.to_s + when Date + obj.to_s + when Time + offset = obj.gmtoff + off_hm = sprintf( + '%+.2d:%.2d', + (offset / 3600.0).to_i, + (offset % 3600.0) / 60 + ) + u_sec = (obj.usec != 0) ? sprintf(".%.6d", obj.usec) : '' + obj.strftime("%Y-%m-%d %H:%M:%S#{u_sec} #{off_hm}") + when Symbol + '!ruby/symbol ' + emit_string(obj.to_s, level) + when Range + '!ruby/range ' + obj.to_s + when Regexp + '!ruby/regexp ' + obj.inspect + else + case + when obj.is_a?(Struct) + struct_members = {} + obj.each_pair{|k, v| struct_members[k.to_s] = v } + '!ruby/struct:' + obj.class.to_s.sub(/^(Struct::(.+)|.*)$/, '\2') + ' ' + + emit(struct_members, level + 1) + else + # serialized as a generic object + object_members = {} + obj.instance_variables.each{|k, v| + object_members[k.to_s.sub(/^@/, '')] = obj.instance_variable_get(k) + } + '!ruby/object:' + obj.class.to_s + ' ' + + emit(object_members, level + 1) + end + end + end + + def emit_string(str, level) + (is_string, is_printable, is_one_line, is_one_plain_line) = string_type(str) + if is_string + if is_printable + if is_one_plain_line + emit_simple_string(str, level) + else + (is_one_line || str.length < @options[:minimum_block_length]) ? + emit_quoted_string(str, level) : + emit_block_string(str, level) + end + else + emit_quoted_string(str, level) + end + else + emit_base64_binary(str, level) + end + end + + def emit_simple_string(str, level) + str + end + + def emit_block_string(str, level) + str = normalize_line_break(str) + + indent = s_indent(level) + indentation_indicator = (str =~ /\A /) ? indent.size.to_s : '' + str =~ /(#{REX_NORMAL_LB}*)\z/ + chomping_indicator = case $1.length + when 0 + '-' + when 1 + '' + else + '+' + end + + str.chomp! + str.gsub!(/#{REX_NORMAL_LB}/) { + $1 + indent + } + '|' + indentation_indicator + chomping_indicator + "\n" + indent + str + end + + def emit_quoted_string(str, level) + str = yaml_escape(normalize_line_break(str)) + if (str.length < @options[:minimum_block_length]) + str.gsub!(/#{REX_NORMAL_LB}/) { ESCAPE_SEQ_LB[$1] } + else + str.gsub!(/#{REX_NORMAL_LB}$/) { ESCAPE_SEQ_LB[$1] } + str.gsub!(/(#{REX_NORMAL_LB}+)(.)/) { + trail_c = $3 + $1 + trail_c.sub(/([\t ])/) { ESCAPE_SEQ_WS[$1] } + } + indent = s_indent(level) + str.gsub!(/#{REX_NORMAL_LB}/) { + ESCAPE_SEQ_LB[$1] + "\\\n" + indent + } + end + '"' + str + '"' + end + + def emit_base64_binary(str, level) + indent = "\n" + s_indent(level) + base64 = [str].pack('m') + '!binary |' + indent + base64.gsub(/\n(?!\z)/, indent) + end + + def string_type(str) + if str.respond_to?(:encoding) && (!str.valid_encoding? || str.encoding == Encoding::ASCII_8BIT) + return false, false, false, false + end + (ucs_codes = str.unpack('U*')) rescue ( + # ArgumentError -> binary data + return false, false, false, false + ) + if ( + @options[:printable_with_syck] && + str =~ /\A#{REX_ANY_LB}* | #{REX_ANY_LB}*\z|#{REX_ANY_LB}{2}\z/ + ) + # detour Syck bug + return true, false, nil, false + end + ucs_codes.each {|ucs_code| + return true, false, nil, false unless is_printable?(ucs_code) + } + return true, true, is_one_line?(str), is_one_plain_line?(str) + end + + def is_printable?(ucs_code) + # YAML 1.1 / 4.1.1. + ( + [0x09, 0x0a, 0x0d, 0x85].include?(ucs_code) || + (ucs_code <= 0x7e && ucs_code >= 0x20) || + (ucs_code <= 0xd7ff && ucs_code >= 0xa0) || + (ucs_code <= 0xfffd && ucs_code >= 0xe000) || + (ucs_code <= 0x10ffff && ucs_code >= 0x10000) + ) && + !( + # treat LS/PS as non-printable characters + @options[:escape_b_specific] && + (ucs_code == 0x2028 || ucs_code == 0x2029) + ) + end + + def is_one_line?(str) + str !~ /#{REX_ANY_LB}(?!\z)/ + end + + def is_one_plain_line?(str) + # YAML 1.1 / 4.6.11. + str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/ && + str !~ /[:\#\s\[\]\{\},]/ && + str !~ /#{REX_ANY_LB}/ && + str !~ /^(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_MERGE} + |#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x + end + + def s_indent(level) + # YAML 1.1 / 4.2.2. + ' ' * (level * @options[:indent_size]) + end + + def normalize_line_break(str) + # YAML 1.1 / 4.1.4. + str.gsub(/(#{REX_CRLF}|#{REX_CR}|#{REX_NEL})/, "\n") + end + + def yaml_escape(str) + # YAML 1.1 / 4.1.6. + str.gsub(/[^a-zA-Z0-9]/u) {|c| + ucs_code, = (c.unpack('U') rescue [??]) + case + when ESCAPE_SEQ[c] + ESCAPE_SEQ[c] + when is_printable?(ucs_code) + c + when @options[:escape_as_utf8] + c.respond_to?(:bytes) ? + c.bytes.collect {|b| '\\x%.2x' % b }.join : + '\\x' + c.unpack('H2' * c.size).join('\\x') + when ucs_code == 0x2028 || ucs_code == 0x2029 + ESCAPE_SEQ_LB[c] + when ucs_code <= 0x7f + sprintf('\\x%.2x', ucs_code) + when ucs_code <= 0xffff + sprintf('\\u%.4x', ucs_code) + else + sprintf('\\U%.8x', ucs_code) + end + } + end + + module Constants + UCS_0X85 = [0x85].pack('U') # c285@UTF8 Unicode next line + UCS_0XA0 = [0xa0].pack('U') # c2a0@UTF8 Unicode non-breaking space + UCS_0X2028 = [0x2028].pack('U') # e280a8@UTF8 Unicode line separator + UCS_0X2029 = [0x2029].pack('U') # e280a9@UTF8 Unicode paragraph separator + + # non-break characters + ESCAPE_SEQ = { + "\x00" => '\\0', + "\x07" => '\\a', + "\x08" => '\\b', + "\x0b" => '\\v', + "\x0c" => '\\f', + "\x1b" => '\\e', + "\"" => '\\"', + "\\" => '\\\\', + } + + # non-breaking space + ESCAPE_SEQ_NS = { + UCS_0XA0 => '\\_', + } + + # white spaces + ESCAPE_SEQ_WS = { + "\x09" => '\\t', + " " => '\\x20', + } + + # line breaks + ESCAPE_SEQ_LB ={ + "\x0a" => '\\n', + "\x0d" => '\\r', + UCS_0X85 => '\\N', + UCS_0X2028 => '\\L', + UCS_0X2029 => '\\P', + } + + # regexps for line breaks + REX_LF = Regexp.escape("\x0a") + REX_CR = Regexp.escape("\x0d") + REX_CRLF = Regexp.escape("\x0d\x0a") + REX_NEL = Regexp.escape(UCS_0X85) + REX_LS = Regexp.escape(UCS_0X2028) + REX_PS = Regexp.escape(UCS_0X2029) + + REX_ANY_LB = /(#{REX_LF}|#{REX_CR}|#{REX_NEL}|#{REX_LS}|#{REX_PS})/ + REX_NORMAL_LB = /(#{REX_LF}|#{REX_LS}|#{REX_PS})/ + + # regexps for language-Independent types for YAML1.1 + REX_BOOL = / + y|Y|yes|Yes|YES|n|N|no|No|NO + |true|True|TRUE|false|False|FALSE + |on|On|ON|off|Off|OFF + /x + REX_FLOAT = / + [-+]?([0-9][0-9_]*)?\.[0-9.]*([eE][-+][0-9]+)? # (base 10) + |[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]* # (base 60) + |[-+]?\.(inf|Inf|INF) # (infinity) + |\.(nan|NaN|NAN) # (not a number) + /x + REX_INT = / + [-+]?0b[0-1_]+ # (base 2) + |[-+]?0[0-7_]+ # (base 8) + |[-+]?(0|[1-9][0-9_]*) # (base 10) + |[-+]?0x[0-9a-fA-F_]+ # (base 16) + |[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60) + /x + REX_MERGE = / + << + /x + REX_NULL = / + ~ # (canonical) + |null|Null|NULL # (English) + | # (Empty) + /x + REX_TIMESTAMP = / + [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] # (ymd) + |[0-9][0-9][0-9][0-9] # (year) + -[0-9][0-9]? # (month) + -[0-9][0-9]? # (day) + ([Tt]|[ \t]+)[0-9][0-9]? # (hour) + :[0-9][0-9] # (minute) + :[0-9][0-9] # (second) + (\.[0-9]*)? # (fraction) + (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? # (time zone) + /x + REX_VALUE = / + = + /x + end + + include Constants +end + +module Puppet::Parser::Functions + newfunction(:sorted_yaml, + :type => :rvalue, + :doc => "This function outputs yaml, but ensures the keys are sorted." + ) do |arguments| + + if arguments.is_a?(Array) + if arguments.size != 1 + raise(Puppet::ParseError, "sorted_yaml(): Wrong number of arguments given (#{arguments.size} for 1)") + end + yaml = arguments.first + else + yaml = arguments + end + return Ya2YAML.new()._ya2yaml(yaml) + end +end -- cgit v1.2.3