From 1105f1fe74d5a0c0f2f3e5fbbef9df8d73267853 Mon Sep 17 00:00:00 2001 From: Justin Stoller Date: Tue, 11 Feb 2020 09:49:24 -0800 Subject: [PATCH 1/2] (wip) use json, not pson, steal Bens work to make pup4 --- .../functions/stdlib/load_module_metadata.rb | 41 +++++++++ lib/puppet/functions/stdlib/loadjson.rb | 84 +++++++++++++++++++ lib/puppet/functions/stdlib/parsejson.rb | 34 ++++++++ .../parser/functions/load_module_metadata.rb | 2 + lib/puppet/parser/functions/loadjson.rb | 2 + lib/puppet/parser/functions/parsejson.rb | 1 + 6 files changed, 164 insertions(+) create mode 100644 lib/puppet/functions/stdlib/load_module_metadata.rb create mode 100644 lib/puppet/functions/stdlib/loadjson.rb create mode 100644 lib/puppet/functions/stdlib/parsejson.rb diff --git a/lib/puppet/functions/stdlib/load_module_metadata.rb b/lib/puppet/functions/stdlib/load_module_metadata.rb new file mode 100644 index 000000000..f9edae72e --- /dev/null +++ b/lib/puppet/functions/stdlib/load_module_metadata.rb @@ -0,0 +1,41 @@ +# +# @summary +# This function loads the metadata of a given module. +# +# @example Example Usage: +# $metadata = load_module_metadata('archive') +# notify { $metadata['author']: } +# +# @return +# The modules metadata +# +Puppet::Functions.create_function(:'stdlib::load_module_metadata') 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 + param 'String', :module_name + optional_param 'Bool', :allow_empty_metadata + end + + def default_impl(module_name, allow_empty_metadata) + module_path = function_get_module_path([module_name]) + metadata_json = File.join(module_path, 'metadata.json') + + if File.exists?(metadata_json) + return Puppet::Util::Json.load(File.read(metadata_json)) + + else + if !allow_empty_metadata + raise(Puppet::ParseError, + "load_module_metadata(): No metadata.json file for module #{module_name}") + end + + return Hash.new + end + end +end diff --git a/lib/puppet/functions/stdlib/loadjson.rb b/lib/puppet/functions/stdlib/loadjson.rb new file mode 100644 index 000000000..f709762fd --- /dev/null +++ b/lib/puppet/functions/stdlib/loadjson.rb @@ -0,0 +1,84 @@ +# +# @summary +# Load a JSON file containing an array, string, or hash, and return the data +# in the corresponding native data type. +# +# The first parameter can be a file path or a URL. +# The second parameter is the default value. It will be returned if the file +# was not found or could not be parsed. +# +# @return [Array|String|Hash] +# The data stored in the JSON file, the type depending on the type of data that was stored. +# +# @example Example Usage: +# $myhash = loadjson('/etc/puppet/data/myhash.json') +# $myhash = loadjson('https://example.local/my_hash.json') +# $myhash = loadjson('https://username:password@example.local/my_hash.json') +# $myhash = loadjson('no-file.json', {'default' => 'value'}) +# +# +Puppet::Functions.create_function(:'stdlib::loadjson') do + require 'puppet/util/json' + + # If URL is in the format of https://username:password@example.local/my_info.json + URI_WITH_NAME_AND_PASS_PATTERN = %r{(http\://|https\://)(.*):(.*)@(.*)} + + # If URL is in the format of https://username@example.local/my_info.json + URI_WITH_NAME_PATTERN = %r{(http\://|https\://)(.*)@(.*)} + + dispatch :default_impl do + param 'String', :path_or_uri + optional_param Any, :default + end + + def default_impl(path_or_uri, default = nil) + begin + if path_or_uri.start_with?('http://', 'https://') + load_from_uri(path_or_uri, default) + + elsif File.exists?(path_or_uri) + content = File.read(path_or_uri) + + Puppet::Util::Json.load(content) || default + else + warning("Can't load '#{path_or_uri}' File does not exist!") + + default + end + rescue StandardError => err + if default + default + else + raise err + end + end + end + + private + def load_from_uri(uri, default) + require 'open-uri' + + username = '' + password = '' + if (match = uri.match(URI_WITH_NAME_AND_PASS_PATTERN)) + protocol, username, password, path = match.captures + url = "#{protocol}#{path}" + elsif (match = uri.match(URI_WITH_NAME_PATTERN)) + protocol, username, path = match.captures + url = "#{protocol}#{path}" + else + url = uri + end + + begin + contents = OpenURI.open_uri(url, :http_basic_authentication => [username, password]) + rescue OpenURI::HTTPError => err + res = err.io + warning("Can't load '#{url}' HTTP Error Code: '#{res.status[0]}'") + + default + end + + Puppet::Util::Json.load(contents) || default + end +end diff --git a/lib/puppet/functions/stdlib/parsejson.rb b/lib/puppet/functions/stdlib/parsejson.rb new file mode 100644 index 000000000..cbdb8a08d --- /dev/null +++ b/lib/puppet/functions/stdlib/parsejson.rb @@ -0,0 +1,34 @@ +# +# @summary +# This function accepts JSON as a string and converts it into the correct +# Puppet structure. +# +# @return +# convert JSON into Puppet structure +# +# > *Note:* +# The optional second argument can be used to pass a default value that will +# be returned if the parsing of JSON string have failed. +# +# +Puppet::Functions.create_function(:'stdlib::parsejson') do + + dispatch :default_impl do + param 'String', :json_string + optional_param 'Any', :default + end + + def default_impl(json_string, default = nil) + require 'puppet/util/json' + + begin + Puppet::Util::Json.load(json_string) || default + rescue StandardError => err + if default + default + else + raise err + end + end + end +end diff --git a/lib/puppet/parser/functions/load_module_metadata.rb b/lib/puppet/parser/functions/load_module_metadata.rb index 79d98cdd8..ba7a22dff 100644 --- a/lib/puppet/parser/functions/load_module_metadata.rb +++ b/lib/puppet/parser/functions/load_module_metadata.rb @@ -15,6 +15,8 @@ module Puppet::Parser::Functions DOC ) do |args| raise(Puppet::ParseError, 'load_module_metadata(): Wrong number of arguments, expects one or two') unless [1, 2].include?(args.size) + + Puppet.deprecation_warning("`load_module_metadata' is deprecated, please use `stdlib::load_module_data' instead") mod = args[0] allow_empty_metadata = args[1] module_path = function_get_module_path([mod]) diff --git a/lib/puppet/parser/functions/loadjson.rb b/lib/puppet/parser/functions/loadjson.rb index 3c6087855..99f007d3b 100644 --- a/lib/puppet/parser/functions/loadjson.rb +++ b/lib/puppet/parser/functions/loadjson.rb @@ -23,6 +23,8 @@ module Puppet::Parser::Functions DOC raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless args.length >= 1 + + Puppet.deprecation_warning("`loadjson' is deprecated, please use `stdlib::loadjson' instead") require 'open-uri' begin if args[0].start_with?('http://', 'https://') diff --git a/lib/puppet/parser/functions/parsejson.rb b/lib/puppet/parser/functions/parsejson.rb index 76b392fb1..b36b239e4 100644 --- a/lib/puppet/parser/functions/parsejson.rb +++ b/lib/puppet/parser/functions/parsejson.rb @@ -18,6 +18,7 @@ module Puppet::Parser::Functions raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless arguments.length >= 1 begin + Puppet.deprecation_warning("`parsejson' is deprecated, please use `stdlib::parsejson' instead") PSON.load(arguments[0]) || arguments[1] rescue StandardError => e raise e unless arguments[1] From d584063b2e18a2e83751b9a7b78d57d80bb3f0ae Mon Sep 17 00:00:00 2001 From: Justin Stoller Date: Wed, 4 Mar 2020 15:11:57 -0800 Subject: [PATCH 2/2] (maint) Use more type dispatch --- lib/puppet/functions/stdlib/loadjson.rb | 81 ++++++++++++++----------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/lib/puppet/functions/stdlib/loadjson.rb b/lib/puppet/functions/stdlib/loadjson.rb index f709762fd..9681bf396 100644 --- a/lib/puppet/functions/stdlib/loadjson.rb +++ b/lib/puppet/functions/stdlib/loadjson.rb @@ -26,25 +26,19 @@ # If URL is in the format of https://username@example.local/my_info.json URI_WITH_NAME_PATTERN = %r{(http\://|https\://)(.*)@(.*)} - dispatch :default_impl do - param 'String', :path_or_uri + dispatch :load_from_url do + param 'Variant[Stdlib::HttpUrl, Stdlib::HttpsUrl]', :url optional_param Any, :default end - def default_impl(path_or_uri, default = nil) - begin - if path_or_uri.start_with?('http://', 'https://') - load_from_uri(path_or_uri, default) - - elsif File.exists?(path_or_uri) - content = File.read(path_or_uri) - - Puppet::Util::Json.load(content) || default - else - warning("Can't load '#{path_or_uri}' File does not exist!") + dispatch :load_local do + param 'Stdlib::Absolutepath', :path + optional_param Any, :default + end - default - end + def with_error_or_default(default = nil, &block) + begin + yield rescue StandardError => err if default default @@ -54,31 +48,46 @@ def default_impl(path_or_uri, default = nil) end end - private - def load_from_uri(uri, default) - require 'open-uri' + def load_local(path, default) + with_error_or_default(default) do + if File.exists?(path) + content = File.read(path) + + Puppet::Util::Json.load(content) || default + else + warning("Can't load '#{path}' File does not exist!") - username = '' - password = '' - if (match = uri.match(URI_WITH_NAME_AND_PASS_PATTERN)) - protocol, username, password, path = match.captures - url = "#{protocol}#{path}" - elsif (match = uri.match(URI_WITH_NAME_PATTERN)) - protocol, username, path = match.captures - url = "#{protocol}#{path}" - else - url = uri + default + end end + end - begin - contents = OpenURI.open_uri(url, :http_basic_authentication => [username, password]) - rescue OpenURI::HTTPError => err - res = err.io - warning("Can't load '#{url}' HTTP Error Code: '#{res.status[0]}'") + def load_from_url(url, default) + with_error_or_default(default) do + require 'open-uri' - default - end + username = '' + password = '' + if (match = url.match(URI_WITH_NAME_AND_PASS_PATTERN)) + protocol, username, password, path = match.captures + url = "#{protocol}#{path}" + elsif (match = url.match(URI_WITH_NAME_PATTERN)) + protocol, username, path = match.captures + url = "#{protocol}#{path}" + else + url = url + end + + begin + contents = OpenURI.open_uri(url, :http_basic_authentication => [username, password]) + rescue OpenURI::HTTPError => err + res = err.io + warning("Can't load '#{url}' HTTP Error Code: '#{res.status[0]}'") + + default + end - Puppet::Util::Json.load(contents) || default + Puppet::Util::Json.load(contents) || default + end end end