Re-using class parameters from other classes

I’m trying to re-use a class parameter defined in another class, but it seems it works only in certain circumstances.

So I’ve got this class (names are somewhat important, since I suspect a problem with alphabetical order):

class p_class (
    var1,
    var2
){}

Values for var1 and var2 as set via smart class parameters. So now I want to use these values in another class. This works (class parameters get set to the values from p_class):

class s_class (
    s_var1 = $p_class::var1,
    s_var2 = $p_class::var2,
) {}

However, this doesn’t (class parameters stay empty):

class l_class (
    l_var1 = $p_class::var1,
    l_var2 = $p_class::var2,
) {}

In either case, a host is assigned either p_class and s_class or p_class and l_class, but not all three.

I suspect that this only works accidentally by s_class being evaluated later than p_class due to alphabetical order, while l_class comes before p_class and does not get those parameters.

The question is: how can I make it work intentionally instead of accidentally? I’ve tried require p_class inside and outside of l_class and also Class['p_class'] -> Class['l_class'] already. Any other ideas I could try?

Hello rassie,

(sadly) what you are trying to do is not how puppet works. Classes and variables are resolved semi-randomly (the order should stick on a host basis)[1]. You can never assume a another class was actually resolved when your class gets evaluated.
requireing a class does does mitigate the problem somewhat (a better way might be to include it though) - however this does not give you access to it’s parameters (but internal variables).

TR;DR, I see thee options here: from best to worst™:

  • Set up a global variable and set the parameter using foreman’s erb parameter rendering
<%= host_param('my_global_variable') %>
  • Use Hiera to set your parameters, AFAIK you’ll should have access to the required class parameters in this way:[2]:
class foo([String] $bar) {}
# hiera.yaml
foo::bar: 'Awesome value'
  • require / ìnclude a class witch sets up a variable in it’s class body:
class foo() {
  $bar = $::my_global_variable
}
# include foo
# notify{$foo::bar:}

HTH, Helge

[1] This is by design of puppet to mitigate the “thundering herd” problem. Here is where puppet shows it’s age
[2] That said, I find it cumbersome to combine Hiera and Foreman

1 Like

Helge, thanks for explaining this sad fact. With that in mind: how would you normally use a smart class parameter in another context, ideally with Foreman? Example follows:

Let’s say, we’ve got some global proxy settings and those are different depending on some separation criteria which is mapped to host groups. These are set by a class parameter from some generic proxy class (can’t use global parameters because of missing matching rules). This is configured by someone else completely and I can safely assume that the proxy class is applied to and is configured correctly for all hosts. What I’m doing is just set up my application using an application class (for which I’d need proxy settings to inject into templates), and would like to re-use configuration from the proxy class.

How does this normally gets solved? Do I need to add Hiera to the mix or is there a proper pattern to use in this case?

Since Puppet 4 it was changed to be compile order instead. AFAIK you can assume that if you use include myclass you can then access all variables on that class. So the following would work:

class p_class (
    var1,
    var2
){}

class s_class {
  include p_class
  $s_var = $p_class::var1
}

I do realize this may not be exactly what you were after. One thing that commonly happens is some profile class that combines it:

class profiles::myprofile($var1, $var2) {
  class { 'p_class':
    var1 => $var1,
    var2 => $var2,
  }

  class { 's_class':
    var1 => $var1,
    var2 => $var2,
  }

  class { 'l_class':
    var1 => $var1,
    var2 => $var2,
  }
}

Then in Foreman you would assign the profiles::myprofile class instead of multiple classes.

1 Like