From 1dc632e7d4af35acd717c1b3b3e38237bb79b8e1 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Fri, 3 Jan 2020 21:40:11 -0800 Subject: [PATCH] Porting functions to the modern Puppet 4.x API --- .../consul_template_sorted_json.rb | 187 ++++++++++++++++++ ...mplate_consul_template_sorted_json_spec.rb | 41 ++++ 2 files changed, 228 insertions(+) create mode 100644 lib/puppet/functions/consul_template/consul_template_sorted_json.rb create mode 100644 spec/functions/consul_template_consul_template_sorted_json_spec.rb diff --git a/lib/puppet/functions/consul_template/consul_template_sorted_json.rb b/lib/puppet/functions/consul_template/consul_template_sorted_json.rb new file mode 100644 index 0000000..9264599 --- /dev/null +++ b/lib/puppet/functions/consul_template/consul_template_sorted_json.rb @@ -0,0 +1,187 @@ +# This is an autogenerated function, ported from the original legacy version. +# It /should work/ as is, but will not have all the benefits of the modern +# function API. You should see the function docs to learn how to add function +# signatures for type safety and to document this function using puppet-strings. +# +# https://puppet.com/docs/puppet/latest/custom_functions_ruby.html +# +# ---- original file header ---- +# Forked from solarkennedy/puppet-consul at commit: 993dd2b +# https://github.com/solarkennedy/puppet-consul/blob/993dd2b80d7d49a20bfe1cb400d2b4113fe3c888/lib/puppet/parser/functions/consul_sorted_json.rb + +require 'json' + +module JSON + class << self + def sorted_generate(obj) + case obj + when NilClass, :undef, Integer, Float, TrueClass, FalseClass, String + return simple_generate(obj) + when Array + array_ret = [] + obj.each do |a| + array_ret.push(sorted_generate(a)) + end + '[' << array_ret.join(',') << ']' + when Hash + ret = [] + obj.keys.sort.each do |k| + ret.push(k.to_json << ':' << sorted_generate(obj[k])) + end + '{' << ret.join(',') << '}' + else + raise Exception, "Unable to handle object of type #{obj.class.name} with value #{obj.inspect}" + end + end + + def sorted_pretty_generate(obj, indent_len = 4) + # Indent length + indent = ' ' * indent_len + loop_num = 0 + + case obj + when NilClass, :undef, Integer, Float, TrueClass, FalseClass, String + return simple_generate(obj) + when Array + array_ret = [] + + # We need to increase the loop count before #each so the objects inside are indented twice. + # When we come out of #each we decrease the loop count so the closing brace lines up properly. + # + # If you start with loop_num = 1, the count will be as follows + # + # "start_join": [ <-- loop_num == 1 + # "192.168.50.20", <-- loop_num == 2 + # "192.168.50.21", <-- loop_num == 2 + # "192.168.50.22" <-- loop_num == 2 + # ] <-- closing brace <-- loop_num == 1 + # + loop_num += 1 + obj.each do |a| + array_ret.push(sorted_pretty_generate(a, indent_len)) + end + loop_num -= 1 + + return "[\n#{indent * (loop_num + 1)}" << array_ret.join(",\n#{indent * (loop_num + 1)}") << "\n#{indent * loop_num}]" + + when Hash + ret = [] + + # This loop works in a similar way to the above + loop_num += 1 + obj.keys.sort.each do |k| + ret.push((indent * loop_num).to_s << k.to_json << ': ' << sorted_pretty_generate(obj[k], indent_len)) + end + loop_num -= 1 + + return "{\n" << ret.join(",\n") << "\n#{indent * loop_num}}" + else + raise Exception, "Unable to handle object of type #{obj.class.name} with value #{obj.inspect}" + end + end # end def + + private + + # simplify jsonification of standard types + def simple_generate(obj) + case obj + when NilClass, :undef + 'null' + when Integer, Float, TrueClass, FalseClass + obj.to_s + else + # Should be a string + # keep string integers unquoted + (obj =~ %r{\A[-]?(0|[1-9]\d*)\z}) ? obj : obj.to_json + end + end + end # end class +end # end module + +# ---- original file header ---- +# +# @summary +# This function takes unsorted hash and outputs JSON object making sure the keys are sorted. +#Optionally you can pass 2 additional parameters, pretty generate and indent length. +# +#*Examples:* +# +# ------------------- +# -- UNSORTED HASH -- +# ------------------- +# unsorted_hash = { +# 'client_addr' => '127.0.0.1', +# 'bind_addr' => '192.168.34.56', +# 'start_join' => [ +# '192.168.34.60', +# '192.168.34.61', +# '192.168.34.62', +# ], +# 'ports' => { +# 'rpc' => 8567, +# 'https' => 8500, +# 'http' => -1, +# }, +# } +# +# ----------------- +# -- SORTED JSON -- +# ----------------- +# +# consul_template_sorted_json(unsorted_hash) +# +# {"bind_addr":"192.168.34.56","client_addr":"127.0.0.1", +# "ports":{"http":-1,"https":8500,"rpc":8567}, +# "start_join":["192.168.34.60","192.168.34.61","192.168.34.62"]} +# +# ------------------------ +# -- PRETTY SORTED JSON -- +# ------------------------ +# Params: data , pretty , indent . +# +# consul_template_sorted_json(unsorted_hash, true, 4) +# +# { +# "bind_addr": "192.168.34.56", +# "client_addr": "127.0.0.1", +# "ports": { +# "http": -1, +# "https": 8500, +# "rpc": 8567 +# }, +# "start_join": [ +# "192.168.34.60", +# "192.168.34.61", +# "192.168.34.62" +# ] +# } +# +# +# +Puppet::Functions.create_function(:'consul_template::consul_template_sorted_json') do + # @param args + # The original array of arguments. Port this to individually managed params + # to get the full benefit of the modern function API. + # + # @return [Data type] + # Describe what the function returns here + # + dispatch :default_impl do + # Call the method named 'default_impl' when this is matched + # Port this to match individual params for better type safety + repeated_param 'Any', :args + end + + + def default_impl(*args) + + + unsorted_hash = args[0] || {} + pretty = args[1] || false + indent_len = args[2].to_i || 4 + + return JSON.sorted_pretty_generate(unsorted_hash, indent_len) << "\n" if pretty + JSON.sorted_generate(unsorted_hash) + + end +end diff --git a/spec/functions/consul_template_consul_template_sorted_json_spec.rb b/spec/functions/consul_template_consul_template_sorted_json_spec.rb new file mode 100644 index 0000000..6ddb9f8 --- /dev/null +++ b/spec/functions/consul_template_consul_template_sorted_json_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'consul_template::consul_template_sorted_json' do + # without knowing details about the implementation, this is the only test + # case that we can autogenerate. You should add more examples below! + it { is_expected.not_to eq(nil) } + +################################# +# Below are some example test cases. You may uncomment and modify them to match +# your needs. Notice that they all expect the base error class of `StandardError`. +# This is because the autogenerated function uses an untyped array for parameters +# and relies on your implementation to do the validation. As you convert your +# function to proper dispatches and typed signatures, you should change the +# expected error of the argument validation examples to `ArgumentError`. +# +# Other error types you might encounter include +# +# * StandardError +# * ArgumentError +# * Puppet::ParseError +# +# Read more about writing function unit tests at https://rspec-puppet.com/documentation/functions/ +# +# it 'raises an error if called with no argument' do +# is_expected.to run.with_params.and_raise_error(StandardError) +# end +# +# it 'raises an error if there is more than 1 arguments' do +# is_expected.to run.with_params({ 'foo' => 1 }, 'bar' => 2).and_raise_error(StandardError) +# end +# +# it 'raises an error if argument is not the proper type' do +# is_expected.to run.with_params('foo').and_raise_error(StandardError) +# end +# +# it 'returns the proper output' do +# is_expected.to run.with_params(123).and_return('the expected output') +# end +################################# + +end