conditional-statementspuppetpuppet-enterprise

Conditonal resource attributes


To install packages, I feed in data from Hiera into a for loop. Some packages require additional arguments. For packages that do not require an argument, I've set the value to undef, however, Chocolatey reads undef and complains.

How do I get the package resource to ignore the install_options attribute when it is blank or undef?

Hiera snippet:

profile::business::packages:
  office365business:
    version: latest
    provider: chocolatey
    arguments: ['/productid:O365BusinessRetail']
  xmind:
    version: latest
    provider: chocolatey
    arguments: undef
  slack:
    version: latest
    provider: chocolatey
    arguments: undef

Class example:

class profile::business(
  Hash $packages,
){
  if $::kernel == 'windows' {
    $packages.each | $key, $value | {
      package { "install_${key}" :
        name            => $key,
        ensure          => $value['version'],
        provider        => $value['provider'],
        install_options => $value['arguments'],
        notify          => Reboot['after_profile_business'],
      }
    }
    reboot { 'after_profile_business' :
      apply   => finished,
      message => 'Reboot: Business profile applied.'
    }
  }
}

The best I can come up with is using an if clause to apply different instances of the package resource with or without install_options, depending on the value of arguments:

$packages.each | $key, $value | {
  if $value['arguments'] != 'undef' {
    package { "install_${key}" :
      name            => $key,
      ensure          => $value['version'],
      provider        => $value['provider'],
      install_options => $value['arguments'],
      notify          => Reboot['after_profile_admin'],
    }
  } else {
    package { "install_${key}" :
      name     => $key,
      ensure   => $value['version'],
      provider => $value['provider'],
      notify   => Reboot['after_profile_admin'],
    }
  }
}

However, this seems rather clunky and I'm hoping someone might be able to show me a better way?

I've seen the Puppet Selector condition example, but I do not know if this will work for me.

T.I.A


Solution

  • This YAML fragment ...

        arguments: undef
    

    ... sets the value of the 'arguments' key to the string 'undef'. That doesn;t mean the same thing on the Puppet side as the Puppet literal undef.

    There are solutions. All of the best, IMO, revolve around representing absence of data via bona fide absence of data. That avoids any need for special reserved words. So suppose your data looked like this, instead:

    profile::business::packages:
      office365business:
        version: latest
        provider: chocolatey
        arguments: ['/productid:O365BusinessRetail']
      xmind:
        version: latest
        provider: chocolatey
      slack:
        version: latest
        provider: chocolatey
    

    Note that there is no entry bearing the arguments key where there are in fact no arguments to specify. If you have been rigorous and thorough about defining data types, then you may need to adjust your data type for these data to accommodate that, but so much the better because that would better describe the actual data semantics. That data modification probably resolves your issue by itself, because looking up a key that does not exist in a hash that does exist should yield undef (and there's also dig() if the undefinedness can occur at a higher level of a deep data structure).

    Consider also, however, that Puppet has a shortcut for declaring that resource property values are drawn from a hash. That won't quite fit your present data because your keys are not the same as the needed property names, but you could either change the keys in your data or map them at the Puppet level. The latter might look like this:

    # Defining the key / property name mappings here makes them clear, and is easy to
    # change if you need to update the mappings
    $mappings = { 'version' => 'ensure', 'arguments' => 'install_options' }
    
    $packages.each |$package, $properties| {
    
      # map the keys appearing in the data to Puppet property names, based on
      # the hash defined above
      $filtered_props = $properties.reduce({}) |$memo, $pair| {
          $mapped_key = $pair[0] in $mappings ? { true => $mappings[$pair[0]], default => $pair[0] }
          $memo + { $mapped_key => $pair[1] }
      }
    
      # one declaration covering all cases
      package { "install_${package}" :
        name     => $package,
        provider => $value['provider'],
        notify   => Reboot['after_profile_admin'],
        *        => $filtered_props,
      }
    }