From c839f749683566e0a822a228806137740c5cbffb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20L=C3=B6nnemark?= <ketl@nsc.liu.se>
Date: Thu, 10 Nov 2022 16:41:23 +0100
Subject: [PATCH] Convert resolve_ipnets to Puppet 4.x API

In Puppet 7, extra methods within a function definition block are no
longer permitted when using the 3.x API.

Attempting to use such functions aborts catalog compilation with:

    Error: Could not retrieve catalog from remote server:
    Error 500 on SERVER: Internal Server Error: org.jruby.exceptions.SecurityError:
    (SecurityError) Illegal method definition of method 'example_function' in source .../example-module/lib/puppet/parser/functions/example_function.rb on line XXX in legacy function.
    See https://puppet.com/docs/puppet/latest/functions_refactor_legacy.html for more information

We fix the error by updating the only function that has extra methods to
use the 4.x API.

This change drops support for Puppet 3.

https://puppet.com/docs/puppet/7/functions_refactor_legacy.html
https://puppet.com/docs/puppet/5.5/functions_legacy.html
https://puppet.com/docs/puppet/7/functions_ruby_overview.html
---
 .../{parser => }/functions/resolve_ipnets.rb  | 154 +++++++++---------
 1 file changed, 75 insertions(+), 79 deletions(-)
 rename lib/puppet/{parser => }/functions/resolve_ipnets.rb (53%)

diff --git a/lib/puppet/parser/functions/resolve_ipnets.rb b/lib/puppet/functions/resolve_ipnets.rb
similarity index 53%
rename from lib/puppet/parser/functions/resolve_ipnets.rb
rename to lib/puppet/functions/resolve_ipnets.rb
index ee08068..3c4f5b5 100644
--- a/lib/puppet/parser/functions/resolve_ipnets.rb
+++ b/lib/puppet/functions/resolve_ipnets.rb
@@ -4,7 +4,79 @@
 
 require 'ipaddr'
 
-module Puppet::Parser::Functions
+# Lookup IP addresses for network specifications.
+#
+# - **Parameters** (in order):
+#
+# :netspecs:  A network specification string, or a list of those.
+#
+# :flags*:  (Optional) Zero, one or more flags detailing the behaviour
+# of resolve_ipnets().  Each flag is a string.  The following flags
+# are supported:
+#
+#     :"ipv4":  Force lookup of hostnames as IPv4 addresses.
+#
+#     :"ipv6":  Force lookup of hostnames as IPv6 addresses.
+#
+#     :"ignoreerrors":  If an address cannot be resolved, return it
+#     unchanged.
+#
+#     :"failerrors":  Raise an error if an address cannot be resolved.
+#     This is the default behaviour.
+#
+#     :"stable":  Always return addresses in the same order, regardless
+#     of in which order the resolver returns them.  The order relative
+#     to the hostnames in the input will be preserved, so if the input
+#     is ["host-a", "host-b"], then the addresses for host-a will come
+#     before those for host-b in the return value.
+#     NOTE: This breaks the address selection order (RFC 3484/6724)
+#     that the getaddrinfo(3) function implements.
+#     NOTE: The stable flag does not help aginst e.g. DNS servers
+#     responding with a different set of addresses each time.
+#
+# Both "ipv4" and "ipv6" may be specified at the same time, in which
+# case both kinds of addresses will be returned, in the order the
+# flags were specifed.  The function will only return an error if
+# neither family resolves.
+#
+# If no address family is specified, then the order of the addresses
+# families will be as determined by the resolver.
+#
+# A network specification is on the form
+#
+#     [family ":"] hostname ["/" netmask]
+#
+# where family is either "ipv4" for IPv4, or "ipv6" for IPv6 (just
+# "v4" and "v6" also works, for backwards compatibility).  For each
+# such network, the hostname part will be resolved into an IP address.
+# If a protocol family is specified, either explicitly in the network
+# specification or as an optional flag parameter to resolve_ipnets(),
+# an address of that type will be returned, otherwise some kind of
+# default will be chosen.  If the protocol family is specified both
+# in the netspec and as a flag parameter, they must match, even when
+# "ignoreerrors" is in effect.
+#
+# If the hostname part already is a numeric IP address of the wanted
+# family, it will be returned canonicalized.
+#
+# If the hostname part is enclosed in square brackets ("[]"), it will
+# be returned unchanged (but with the brackets removed).  This can be
+# useful if you want some of the addresses resolved and some not.
+#
+# If the netmask part is given, that will be included in the result
+# unchanged and uninterpreted (including the separating slash), without
+# any error checking.  The protocol family part is always removed.
+#
+# The return value will always be a flat list of IP addresses with
+# duplicates removed, each address represented as a string (e.g,
+# "192.168.1.17" or "2001:db8:4711::1:17/96").  Note that each input
+# hostname can result in multiple addresses (although when Ruby 1.8.5
+# is used, only a single address is returned per hostname).
+#
+# Compatibility note: Older versions returned a value that had the
+# same structure as the netspecs parameter; only a single address per
+# hostname was returned; and duplicates were not removed.
+Puppet::Functions.create_function(:resolve_ipnets) do
 
     RESOLVE_IPNETS__IPFAMILIES = {
 	:__ANY__    => Socket::AF_UNSPEC,	# Internal use
@@ -97,84 +169,8 @@ module Puppet::Parser::Functions
 	end
 	return ipaddrs.collect {|ip| ip + maskspec }
     end
-    module_function :resolve_ipnets__netspec
 
-
-    newfunction(:resolve_ipnets, :type => :rvalue, :doc => '\
-	Lookup IP addresses for network specifications.
-
-	- **Parameters** (in order):
-
-	:netspecs:  A network specification string, or a list of those.
-
-	:flags*:  (Optional) Zero, one or more flags detailing the behaviour
-	of resolve_ipnets().  Each flag is a string.  The following flags
-	are supported:
-
-	    :"ipv4":  Force lookup of hostnames as IPv4 addresses.
-
-	    :"ipv6":  Force lookup of hostnames as IPv6 addresses.
-
-	    :"ignoreerrors":  If an address cannot be resolved, return it
-	    unchanged.
-
-	    :"failerrors":  Raise an error if an address cannot be resolved.
-	    This is the default behaviour.
-
-	    :"stable":  Always return addresses in the same order, regardless
-	    of in which order the resolver returns them.  The order relative
-	    to the hostnames in the input will be preserved, so if the input
-	    is ["host-a", "host-b"], then the addresses for host-a will come
-	    before those for host-b in the return value.
-	    NOTE: This breaks the address selection order (RFC 3484/6724)
-	    that the getaddrinfo(3) function implements.
-	    NOTE: The stable flag does not help aginst e.g. DNS servers
-	    responding with a different set of addresses each time.
-
-	Both "ipv4" and "ipv6" may be specified at the same time, in which
-	case both kinds of addresses will be returned, in the order the
-	flags were specifed.  The function will only return an error if
-	neither family resolves.
-
-	If no address family is specified, then the order of the addresses
-	families will be as determined by the resolver.
-
-	A network specification is on the form
-
-	    [family ":"] hostname ["/" netmask]
-
-	where family is either "ipv4" for IPv4, or "ipv6" for IPv6 (just
-	"v4" and "v6" also works, for backwards compatibility).  For each
-	such network, the hostname part will be resolved into an IP address.
-	If a protocol family is specified, either explicitly in the network
-	specification or as an optional flag parameter to resolve_ipnets(),
-	an address of that type will be returned, otherwise some kind of
-	default will be chosen.  If the protocol family is specified both
-	in the netspec and as a flag parameter, they must match, even when
-	"ignoreerrors" is in effect.
-
-	If the hostname part already is a numeric IP address of the wanted
-	family, it will be returned canonicalized.
-
-	If the hostname part is enclosed in square brackets ("[]"), it will
-	be returned unchanged (but with the brackets removed).  This can be
-	useful if you want some of the addresses resolved and some not.
-
-	If the netmask part is given, that will be included in the result
-	unchanged and uninterpreted (including the separating slash), without
-	any error checking.  The protocol family part is always removed.
-
-	The return value will always be a flat list of IP addresses with
-	duplicates removed, each address represented as a string (e.g,
-	"192.168.1.17" or "2001:db8:4711::1:17/96").  Note that each input
-	hostname can result in multiple addresses (although when Ruby 1.8.5
-	is used, only a single address is returned per hostname).
-
-	Compatibility note: Older versions returned a value that had the
-	same structure as the netspecs parameter; only a single address per
-	hostname was returned; and duplicates were not removed.
-    ') \
-    do |args|
+    def resolve_ipnets(*args)
 	if args.length < 1
 	    raise(Puppet::ParseError,
 		  ("resolve_ipnets(): " +
@@ -203,7 +199,7 @@ module Puppet::Parser::Functions
 	end
 	res = []
 	[netspecs].flatten.each do |netspec|
-	    res += Puppet::Parser::Functions::resolve_ipnets__netspec(
+	    res += resolve_ipnets__netspec(
 		netspec, ipfamilies, on_error, do_partsort)
 	end
 	return res.uniq
-- 
GitLab