On 2018-06-12 22:14, Tim Mooney wrote:
In regard to: Re: [Puppet Users] class parameters that depend on other...:

On 2018-06-12 00:55, Tim Mooney wrote:

[snip some of my original context]

Here's an example:

modules/sandbox/manifests/init.pp:
#
# This module exists only to serve as a sandbox where we can experiment with
# puppet code.
#
class sandbox(
   Enum['typeA', 'typeB', 'combined'] $server_type  = 'combined',
   String                             $service_name = $::sandbox::params::service_name,
) inherits ::sandbox::params {

   notice("sandbox initialized.\n")

   notice("\$server_type  = ${server_type}\n")
   notice("\$service_name = ${service_name}\n")

}

modules/sandbox/manifests/params.pp:
#
# a 'sandbox' class for the params pattern.
#
class sandbox::params {

   $_server_type_actual = $::sandbox::server_type

   case $_server_type_actual {
     'combined': {
       $service_name = 'sandbox-typeA+typeB'
     }
     'typeA': {
       $service_name = 'sandbox-typeA'
     }
     'typeB': {
       $service_name = 'sandbox-typeB'
     }
     default: {
       fail("\n\nsandbox::server_type must be one of: combined, typeA, typeB\n")
     }
   }

}

Hopefully the *intent* is relatively clear: provide an intelligent default
value for $service_name based on what the value is for $server_type, but
allow our "intelligent default" value to be overridden.  If
'sandbox::server_type' is set to 'typeB' in our hiera hierarchy, I want
the *default* value for 'sandbox::service_name' to become 'sandbox-typeB'.
If the person configuring the machine needs to override that too, they
should be able to, but setting just the first setting should provide
suitable defaults for the others.

[snip some of my original context]

For starters you do not really need a params.pp or inheritance. Simply configure all parameters in hiera.

Then, you can produce the default value by calling a function. For example like this:

 function mymodule::default_from_a($x) {
   if $x == 'type-A' {
     'sandbox-typeA'
   }
 }
 class example($a, $b = mymodule::default_from_a($a)) {
   notice $a
   notice $b
 }
 class {example: a => 'type-A' }

Thanks for your response Henrik!  The function use is an interesting
approach that I would not have considered.  It works well in the simple
example I presented and accomplishes what I was trying to do.

I'm not sure how I would scale this to something that's "real world" in
size, though.

Let's say you had *many* parameters that you wanted to set defaults for
(but allow overrides on an individual basis) based on a single parameter.
In my environment, the best example where we've used this is to support
the alternate versions of particular packages that are available for
RHEL (and respins) using Software Collections Library (SCL).

For example, on RHEL 6, the standard packages provide php 5.3.3 (plus
some backports and vendor "special sauce").

However, there are alternate versions available from SCL:

     php54-php

     php55-php

     rh-php56-php

     rh-php70-php

So to support various web application requirements, we have modules
that do stuff like (sorry for the lengthy code):

   if $facts['os']['family'] == 'RedHat' {
     if $facts['os']['release']['major'] == '5' {
       #
       # There's no easy httpd 2.4 option for RHEL 5, so barf
       #
       fail("\n\nRHEL 5 is not supported.\n\n")
     } elsif $facts['os']['release']['major'] == '6' {

       # PHP-related settings.
       $php_variant          = hiera('scl::php', 'php54')

       if $php_variant == 'php54' or $php_variant == 'php55' {
        # don't need to include the fpm package, as the phpfpm class gets it
         $php_extra_packages   = hiera('scl::php::extra_packages',
                               [
                               "${php_variant}-runtime",
                               $php_variant,
                               "${php_variant}-php-cli",
                               "${php_variant}-php-ldap",
                               "${php_variant}-php-mbstring",
                               "${php_variant}-php-pdo",
                               ])
         $php_fpm_package_name = "${php_variant}-php-fpm"
         $php_fpm_service_name = $php_fpm_package_name
        $php_fpm_pool_dir     = "/opt/rh/${php_variant}/root/etc/php-fpm.d"         $php_fpm_pid_file     = "/opt/rh/${php_variant}/root/var/run/php-fpm/php-fpm.pid"
         $php_config_dir       = "/opt/rh/${php_variant}/root/etc"
       } elsif $php_variant != 'UNDEF' {
         #
        # for version 5.6.x and later, the name may include rh- at the start
         # and many of the paths have changed.
         #
        # don't need to include the fpm package, as the phpfpm class gets it
         $php_extra_packages   = hiera('scl::php::extra_packages',
                               [
                               "${php_variant}-runtime",
                               $php_variant,
                               "${php_variant}-php-cli",
                               "${php_variant}-php-ldap",
                               "${php_variant}-php-mbstring",
                               "${php_variant}-php-pdo",
                               ])
         $php_fpm_package_name = "${php_variant}-php-fpm"
         $php_fpm_service_name = $php_fpm_package_name
         $php_fpm_pool_dir     = "/etc/opt/rh/${php_variant}/php-fpm.d"
        $php_fpm_pid_file     = "/var/opt/rh/${php_variant}/run/php-fpm/php-fpm.pid"
         $php_config_dir       = "/etc/opt/rh/${php_variant}"
       }
     } else {

       # hack to work around no support for true undef in hiera
       $php_variant            = hiera('scl::php', 'UNDEF')
       #
       # verify that $php_variant is either UNDEF or matches one of:
       #
       #   php55
       #   rh-php56
       #   rh-php70
       #
       validate_re($php_variant, [
                     '^UNDEF$',
                     '^php55$',
                     '^rh-php56$',
                     '^rh-php70$'
                   ],
                  'scl::php must be one of: UNDEF, php55, rh-php56, rh-php70'
       )

       if $php_variant == 'UNDEF' {
         #
         # RHEL 7 or later, with default php from the base operating system
         #

        # don't need to include the fpm package, as the phpfpm class gets it
         $php_extra_packages   = hiera('php::extra_packages',
                                 [
                                   'php-cli',
                                   'php-ldap',
                                   'php-mbstring',
                                   'php-pdo',
                                 ])
         $php_fpm_package_name = 'php-fpm'
         $php_fpm_service_name = $php_fpm_package_name
         $php_fpm_pool_dir     = '/etc/php-fpm.d'
         $php_fpm_pid_file     = '/var/run/php-fpm/php-fpm.pid'
         $php_config_dir       = '/etc'
       } elsif $php_variant == 'php55' {
         #
         # RHEL 7, with SCL php55
         #

        # don't need to include the fpm package, as the phpfpm class gets it
         $php_extra_packages   = hiera('scl::php::extra_packages',
                                 [
                                   "${php_variant}-runtime",
                                   $php_variant,
                                   "${php_variant}-php-cli",
                                   "${php_variant}-php-ldap",
                                   "${php_variant}-php-mbstring",
                                   "${php_variant}-php-pdo",
                                 ])
         $php_fpm_package_name = "${php_variant}-php-fpm"
         $php_fpm_service_name = $php_fpm_package_name
        $php_fpm_pool_dir     = "/opt/rh/${php_variant}/root/etc/php-fpm.d"         $php_fpm_pid_file     = "/opt/rh/${php_variant}/root/var/run/php-fpm/php-fpm.pid"
         $php_config_dir       = "/opt/rh/${php_variant}/root/etc"
       } else {
         #
         # RHEL 7 or later, php from SCL, using the newer naming and layout
         # conventions.
         #
        # don't need to include the fpm package, as the phpfpm class gets it
         $php_extra_packages   = hiera('scl::php::extra_packages',
                               [
                               "${php_variant}-runtime",
                               $php_variant,
                               "${php_variant}-php-cli",
                               "${php_variant}-php-ldap",
                               "${php_variant}-php-mbstring",
                               "${php_variant}-php-pdo",
                               ])
         $php_fpm_package_name = "${php_variant}-php-fpm"
         $php_fpm_service_name = $php_fpm_package_name
         $php_fpm_pool_dir     = "/etc/opt/rh/${php_variant}/php-fpm.d"
        $php_fpm_pid_file     = "/var/opt/rh/${php_variant}/run/php-fpm/php-fpm.pid"
         $php_config_dir       = "/etc/opt/rh/${php_variant}"
       }
     }
   } else {
       fail("Unsupported osfamily=${$facts['os']['family']}\n")
   }



As you can see, there are potentially dozens of settings that depend on
the "big feature switch" of 'scl::php', and we need to be able to override
some or all of the defaults.

While it would be possible to write a function to allow for each and
every one of these to have a dynamic default based on the setting
of scl::php, it's going to be fairly confusing to some of my coworkers
that don't work with puppet as much as I do.  Maybe that's just the
trade-off I have to make to support this kind of thing.

Ultimately, I'm trying to modernize some of the now outdated practices
we've adopted in our modules, with an eye toward current best practices.
I don't want to have to keep explaining to our casual puppet users why
our modules look so different from stuff they see when they look at the
docs or examine a module from the forge.


Yeah, you cannot extend my simple example with one function being called for one parameter to the general case of many parameters and possibly with cross dependencies between them. While simpler cases would work it would not look nice with a function call per parameter.

An alternative that almost works is to write a hiera backend, but it
cannot take given values into account so would only be able to compute
a default from "master-values" that come from hiera (or already assigned
variables - as it has no access to the context in which given values are being bound to parameters of a class).

You could have a wrapper class that is the API, inside of it you would compute all of the defaults and delegate to a private class to do the actual work. This is kind of what params.pp pattern does but via inheritance.

You could change the API of the class such that it accepts a struct.
You also write one function that computes the defaults given such a struct. Users use both.

type MyStruct = Struct[a => Integer, Optional[b] => String]

class example( MyStruct $params) {
  notice $params
}

function with_mystruct_defaults(MyStruct $input) >> MyStruct {
  # given content of $input, returns the computed resulting hash
  # with default values filled in
  $input + case [$input['b'], $input['a']] {
    [undef, Integer[101]]        : { {'b' => 'x-large'} }
    [undef, Integer[11,100]]     : { {'b' => 'large'} }
    [undef, Integer]             : { {'b' => 'medium'} }
    default                      : { { } }
  }
}

# Declare it
class { 'example':
  params => with_mystruct_defaults('a' => 42)
}

When running that it produces this:

Notice: Scope(Class[Example]): {a => 42, b => large}

If you want to use that with a class that does not use the struct:

class example(Integer $a, String $b) { }
# Declare it
class { 'example':
  * => with_mystruct_defaults('a' => 42)
}

As that expands the struct into individual parameter value settings.
the downside is that you have to maintain the set of parameter names in both a struct and as individual parameters.

Hope one of those examples gives you some inspiration.

Best,
- henrik

Thanks,

Tim


--

Visit my Blog "Puppet on the Edge"
http://puppet-on-the-edge.blogspot.se/

--
You received this message because you are subscribed to the Google Groups "Puppet 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to puppet-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/puppet-users/pfqh0a%24fsc%241%40blaine.gmane.org.
For more options, visit https://groups.google.com/d/optout.

Reply via email to