How to set a puppet class variable from within a hiera_hash each loop?

743 Views Asked by At

hiera data

ae::namespace_by_fqdn_pattern:
  '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/blah/regression/client'
  '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/blah/regression/server'

class

class ae {
    $namespace              = hiera('ae::namespace')
    $target_host_patterns   = hiera('ae::target_host_patterns')
    hiera_hash('ae::namespace_by_fqdn_pattern').each |String $pattern, String $ns| {
        if $facts['networking']['fqdn'].match($pattern) {
            $ae::namespace = "${ns}"
        }
    }
    <snip>

... yields

Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Illegal attempt to assign to 'ae::enforcerd_namespace'. Cannot assign to variables in other namespaces (file: /etc/puppetlabs/code/environments/ar/modules/ae/manifests/init.pp, line: 21, column: 13) on node dfw-ubuntu1604-client02.pp-devcos.us-central1.gcp.dev.blah.com

... anyone here know how to do this correctly? trying to conditionally override that $ae::namespace variable but i'm too puppet-ignorant to know how to get it working : (

the loop and the pattern matching bits work. just can't figure out how to correctly set that class variable from within the hiera_hash().each loop.

2

There are 2 best solutions below

0
On

so the solution i landed on was to change the hiera data to:

ae::namespace             : '/test/blah/regression'
ae::namespace_patterns: ['((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com', '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com']
ae::namespace_by_pattern:
  '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/paypal/regression/client'
  '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/paypal/regression/server'

then the class code to:

$pattern = hiera_hash('ae::namespace_patterns').filter |$pattern| {
        $facts['networking']['fqdn'] =~ $pattern
    }
    if length($pattern) {
        $namespace = hiera('ae::namespace_by_pattern')[$pattern[0]]
    } else {
        $namespace = hiera('ae::namespace')
    }

definitely still open to better answers. just what my own hacking produced as workable so far through much trial and error.

0
On

How to set a puppet class variable from within a hiera_hash each loop?

You cannot. The associated block of an each() call establishes a local scope for each iteration. Variable assignments within apply to that local scope, and therefore last only for the duration of one execution of the block. You cannot anyway assign a new value to a variable during its lifetime, so even if you could assign to a class variable from within an each() call, it would be difficult to use that capability (and your approach would not work).

There are several ways you could approach the problem without modifying the form of the data. You could leverage the filter() function, for example, but my personal recommendation would be to use the reduce() function, something like this:

$namespace = lookup('ae::target_host_patterns').reduce(lookup('ae::namespace')) |$ns, $entry| {
  $facts['networking']['fqdn'].match($entry[0]) ? { true => $entry[1], default => $ns }
}

That does pretty much exactly what your original code seems to be trying to do, except that the selected namespace is returned by the reduce() call, to be assigned to a variable by code at class scope, instead of the lambda trying to assign it directly. Note also that it takes care not only of testing the patterns but of assigning the default namespace when none of the patterns match, as it needs to do because you can only assign to the namespace variable once.