About the "params" pattern

175 views
Skip to first unread message

Francois Lafont

unread,
Sep 16, 2014, 8:00:44 PM9/16/14
to puppet...@googlegroups.com
Hi,

About the "params" pattern, I had read this article :

http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/

Suppose I want to use the "params" pattern in my modulees (ok there
really is no *better* way but imagine I want to use this way). In
the article, the author explains that, for the "top level" class of
my module, I can use this way :

class my_module (
$var1 = $my_module::params::var1,
$var2 = $my_module::params::var2,
$var3 = $my_module::params::var3,
...
) inherits my_module::params {
# The body of the class
}

Ok, I understand that. But what about an internal class of my module?
If I want to define an internal class (my_module/internal.pp) which
will be declared in the "my_module" class, what is the "best" way?

In the internal class, I want probably to use some parameters of
the "my_module" class and some variables of the my_module::params
class. How can I do that? Like below? I have doubts because in this
cas I have lot of inheritance and I understand that it was not a
good thing.

# Definition of the my_module::internal class
# I need to the value of $var2 and $var3 of "he "top level" class my_module
# and I need to parameters in my_module::params.
class my_module::internal (
$var2,
$var3,
) inherits my_module::params {
# The body of the class
}


# Declaration of my_module::internal class in the my_module class
class my_module (
$var1 = $my_module::params::var1,
$var2 = $my_module::params::var2,
$var3 = $my_module::params::var3,
...
) inherits my_module::params {
# The body of the class


class my_module::internal {
var2 => $var2,
var3 => $var3,
}

}

Thanks in advance for your help.
François Lafont

Sebastian Otaegui

unread,
Sep 16, 2014, 10:26:51 PM9/16/14
to puppet...@googlegroups.com

As far as I know "inner classes" are an anti pattern(for the lack of a better term)

Each class should live in its own file.

More than one class per file is discouraged

--
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...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/5418CF25.8070101%40gmail.com.
For more options, visit https://groups.google.com/d/optout.

Francois Lafont

unread,
Sep 17, 2014, 4:15:44 AM9/17/14
to puppet...@googlegroups.com
Le 17/09/2014 04:26, Sebastian Otaegui a écrit :
> As far as I know "inner classes" are an anti pattern(for the lack of a
> better term)
>
> Each class should live in its own file.
>
> More than one class per file is discouraged

I agree. It's better to define "inner classes" in separate files
(my_module::internal1 in my_module/internal1.pp, my_module::internal2
in my_module/internal2.pp etc).

But this is not really my question. My question is: which pattern should
I use:
a) to define inner classes (indeed in a specific file)
b) and to call inner classes in the "my_module" class (which is the
"top level" class called by the user of my_module)

knowing that a inner class needs to use variables defined in
my_module::params, but among these variables, maybe severals
variables are parameters of the my_module class with a different
value of the default value proposed in my_module::params.

For instance with:

class my_module (
$var1 = $my_module::params::var1,
$var2 = $my_module::params::var2,
$var3 = $my_module::params::var3,
) inherits my_module::params {
# The body of the class
}

and:

class my_module::params {

## API of the module ##
$var1 = default_value1
$var2 = default_value2
$var3 = default_value3

## shared variables between the classes of my_module
## but they are not part of the API module
$foo1 = default_value_foo1
$foo2 = default_value_foo2
$foo3 = default_value_foo3
...
}

Imagine I want to define an inner class "internal" which
needs to use $var2, $var3, $foo1 and $foo2. Which pattern
should I use to define my "internal" class.

This one?

class my_module::internal (
$var2,
$var3,
) inherits my_module::params {

notify { "value of foo1 -> $foo1": }
notify { "value of foo2 -> $foo2": }

}

This one?

class my_module::internal (
$var2,
$var3,
) {

require my_module::params
notify { "value of foo1 -> $my_module:params::foo1": }
notify { "value of foo2 -> $my_module:params::foo2": }

}

Or maybe another one...?

In the article that I pointed out in my first message, the
parttern to use to define the "top level" class is clearly
explained. This is:

class my_module (
# API of my_module
$var1 = $my_module::params::var1,
$var2 = $my_module::params::var2,
$var3 = $my_module::params::var3,
) inherits my_module::params {

# The body of the class

}

But the article doesn't explain the pattern to use to define
an inner class (which needs to use variables defined in
my_module::params too and needs to use some variables in
the parameters of my_module class).

I hope I'm clear in my question.
François Lafont


Francois Lafont

unread,
Sep 17, 2014, 7:22:52 AM9/17/14
to puppet...@googlegroups.com
In fact, It's curious. I have made some tests with puppet 3.7.0
on Debian Wheezy and I can simply do this:

class my_module::params {

## API of the module ##
$var1 = default_value1
$var2 = default_value2

## shared variables between the classes of my_module
## but they are not part of the API module
$foo1 = default_value_foo1
$foo2 = default_value_foo2
...
}

class my_module (
$var1 = $my_module::params::var1,
$var2 = $my_module::params::var2,
) inherits my_module::params {

include my_module

}

class my_module::internal {

file { '/tmp/test_internal.txt':
content => template('test/test_internal.txt.erb'),
}

notify { 'm1': message => "var1 = ${my_module::var1}", }
notify { 'm2': message => "var2 = ${my_module::var2}", }
notify { 'm3': message => "foo1 = ${my_module::foo1}", }
notify { 'm4': message => "foo2 = ${my_module::foo2}", }

}

It's curious because I use no inheritance in my_module::internal,
no require etc. and despite that I can access to the variables of
my_module. Is it normal?

One thing very curious too: in the my_module::internal class
I can access to $var1 with this syntax :

${my_module::var1}

and $var1 is empty. Ok, I think I understand that. But in my
template "test_internal.txt.erb" I can use directly:

blabla <%= @var1 %> blabla

without the fully qualified name. Here, I don't understand.

Anyway, although I do not understand well the "scope" mechanism
etc. I feel that I can just use this design for inner classes :

class my_module::internal {

# No inheritance, no require of my_module::params etc.
# It's unnecessary.

# In the class, I must use the fully qualified name
# to access to a variable of my_module inherited by
# my_module::params (and sometimes overwritten).
# For instance:
notify { 'm1': message => "var1 = ${my_module::var1}", }

# But in templates, I can directy use @var1.

}

Am I wrong?

François Lafont

jcbollinger

unread,
Sep 17, 2014, 10:49:27 AM9/17/14
to puppet...@googlegroups.com


On Wednesday, September 17, 2014 3:15:44 AM UTC-5, François Lafont wrote:
Le 17/09/2014 04:26, Sebastian Otaegui a écrit :
> As far as I know "inner classes" are an anti pattern(for the lack of a
> better term)
>


Nested classes are poor form, but multiple classes in the same module are fine.

 
> Each class should live in its own file.
>
> More than one class per file is discouraged



More specifically, nesting classes or defined types inside other classes is widely considered poor form these days, and it will cause you grief unless you know exactly what you're doing or you get very, very lucky.  Putting multiple un-nested classes or definitions in the same file (or otherwise arranging classes and definitions differently than the autoloader is prepared to handle) is even worse form, and even if you know what you're doing you probably cannot make that work smoothly without resorting to near-deprecated 'import'.

 
I agree. It's better to define "inner classes" in separate files


I prefer the terminology "module classes", as distinguished from the "module main class" or "main module class".  The term "inner class" carries too much baggage, and anyway, sometimes module classes are not intended to be private to the module.

 
(my_module::internal1 in my_module/internal1.pp, my_module::internal2
in my_module/internal2.pp etc).

But this is not really my question. My question is: which pattern should
I use:
    a) to define inner classes (indeed in a specific file)
    b) and to call inner classes in the "my_module" class (which is the
       "top level" class called by the user of my_module)

knowing that a inner class needs to use variables defined in
my_module::params, but among these variables, maybe severals
variables are parameters of the my_module class with a different
value of the default value proposed in my_module::params.



Do not get hung up on class inheritance, and especially do not confuse the usage of the terms "class" and "inheritance" in Puppet context with the usage of the same terms in OOP context.  Puppet classes are not classes in the OO sense, and Puppet class inheritance has significant dissimilarities from typical OO class inheritance.

Moreover, with respect to your actual question, it is important to understand the purpose of class inheritance as it applies to the params class pattern: its only purpose in that context is to ensure that the ::params class is evaluated (and thus its variables assigned their values) before the parameter list of the inheriting class is evaluated.  That makes it safe for variables of the ::params class to be used as class parameter defaults in the inheriting class.  Note, too, that that effect is merely a side effect of class inheritance, not its true purpose.

If a given module class uses variables of the module's ::params class, but none of them as (its own) parameter defaults, then it can 'include my_module::params' in its body instead of inheriting from the params class.  If a module class does use ::params class variables as default values for its own parameters, however, then it should inherit from the params class both for documentary and for self-containment purposes.

 


No, if only because you are using the variables $my_module::params::foo1 and $my_module::params::foo2 without qualifying their names.  It works, but it is poor form.  In this particular case it is also confusing, because class parameters $my_module::internal::var2 and $my_module::internal::var3 have the same unqualified names as variables of class my_module::params, but they are completely separate variables.
 

This one?

class my_module::internal (
  $var2,
  $var3,
) {
 
  require my_module::params
  notify { "value of foo1 -> $my_module:params::foo1": }
  notify { "value of foo2 -> $my_module:params::foo2": }

}



It would be better to use 'include' than 'require' in this case, and you mis-typed a couple of your namespace separators, but basically that's fine.

 
Or maybe another one...?



You could also do this:

class my_module::internal (
  $var2,
  $var3,
) inherits my_module::params {
  notify { "value of foo1 -> $my_module::params::foo1": }
  notify { "value of foo2 -> $my_module::params::foo2": }
}


I'm not a big fan of the ::params class pattern in the first place, but if you use it then there is some maintenance advantage in keeping all your code consistent that way.


John

Atom Powers

unread,
Sep 17, 2014, 10:59:55 AM9/17/14
to puppet...@googlegroups.com
This is what I do. This way the sub-classes automatically get variables defaults from the parent-class and when using the module you should only have to call the parent class; as long as you have defined all your parameters that are used  by any sub-model in the parent class.

class module (
 $var0 = $::module::params::var0,
 $var1 = $::module::params::var1,

) inherits ::module::params {
include module::file 
include module::package
 }

class module::file (
 $var0 = $::module::var0
 ) {  }

class module::package (
 $var1 = $::module::var1
 ) {  }

--
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...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Perfection is just a word I use occasionally with mustard.
--Atom Powers--

jcbollinger

unread,
Sep 17, 2014, 11:12:49 AM9/17/14
to puppet...@googlegroups.com


On Wednesday, September 17, 2014 6:22:52 AM UTC-5, François Lafont wrote:

 
In fact, It's curious. I have made some tests with puppet 3.7.0
on Debian Wheezy and I can simply do this:

class my_module::params {

  ## API of the module ##
  $var1 = default_value1
  $var2 = default_value2

  ## shared variables between the classes of my_module
  ## but they are not part of the API module
  $foo1 = default_value_foo1
  $foo2 = default_value_foo2
  ...
}

class my_module (
  $var1 = $my_module::params::var1,
  $var2 = $my_module::params::var2,
) inherits my_module::params {

  include my_module


There's no point to including class my_module inside its own definition.  Did you mean to include my_module::internal?

 

}

class my_module::internal {

  file { '/tmp/test_internal.txt':
    content => template('test/test_internal.txt.erb'),
  }

  notify { 'm1': message => "var1 = ${my_module::var1}", }
  notify { 'm2': message => "var2 = ${my_module::var2}", }
  notify { 'm3': message => "foo1 = ${my_module::foo1}", }
  notify { 'm4': message => "foo2 = ${my_module::foo2}", }

}

It's curious because I use no inheritance in my_module::internal,
no require etc. and despite that I can access to the variables of
my_module. Is it normal?



It is very poor form to access $my_module::params::foo1 and $my_module::params::foo2 via inheriting class my_module (i.e. as ${my_module::foo1}, etc.).  With that out of the way, it is better to say that the result you observe is among the valid possibilities than to say it is "normal".

All class variables have global visibility regardless of inheritance or declaration (e.g. via include / require).  The main purpose of all that as it pertains to variables is to ensure that Puppet evaluates classes during catalog compilation in an order that assigns values to those variables before those values are used.  It also serves a documentary purpose, showing clearly that one class has a dependency on another.

 
One thing very curious too: in the my_module::internal class
I can access to $var1 with this syntax :

    ${my_module::var1}


No, you can't.  There is no $var1 in class my_module::internal, so you are not accessing such a variable.  You are instead accessing $my_module::params::var1 via an alias (that alias resulting from class my_module inheriting my_module::params).  Don't use that alias, even in class my_module.

 

and $var1 is empty. Ok, I think I understand that. But in my
template "test_internal.txt.erb" I can use directly:

    blabla <%= @var1 %> blabla

without the fully qualified name. Here, I don't understand.


I don't believe you.  That form of reference would work of class my_module::internal were inheriting my_module::params (or class my_module) because of the aliasing mechanism I described.  It would also work if my_module::internal had a parameter or an ordinary variable of its own named "var1".  It will not work with the code you actually presented.

 

Anyway, although I do not understand well the "scope" mechanism
etc. I feel that I can just use this design for inner classes :

class my_module::internal {

  # No inheritance, no require of my_module::params etc.
  # It's unnecessary.



No, not a safe assumption.

 
  # In the class, I must use the fully qualified name
  # to access to a variable of my_module inherited by
  # my_module::params (and sometimes overwritten).
  # For instance:
  notify { 'm1': message => "var1 = ${my_module::var1}", }



Yes and no.  Do always use fully-qualified names to reference variables of other classes, even if you inherit from their classes.  Do not reference class variables via inheritance-related aliases, however.  To some extent those are requirements, and to some extent just good practices.

 
  # But in templates, I can directy use @var1.



Only if there is a global or local variable with that name in the scope from which the template is evaluated.

 
}

Am I wrong?

 

I'm afraid so, as described already above.


John

Francois Lafont

unread,
Sep 17, 2014, 11:55:12 PM9/17/14
to puppet...@googlegroups.com
Thank you for your answer.

Le 17/09/2014 17:12, jcbollinger a écrit :

>> class my_module (
>> $var1 = $my_module::params::var1,
>> $var2 = $my_module::params::var2,
>> ) inherits my_module::params {
>>
>> include my_module
>>
>
>
> There's no point to including class my_module inside its own definition.
> Did you mean to include my_module::internal?

Yes exactly. Sorry for the mistake.

>> class my_module::internal {
>>
>> file { '/tmp/test_internal.txt':
>> content => template('test/test_internal.txt.erb'),
>> }
>>
>> notify { 'm1': message => "var1 = ${my_module::var1}", }
>> notify { 'm2': message => "var2 = ${my_module::var2}", }
>> notify { 'm3': message => "foo1 = ${my_module::foo1}", }
>> notify { 'm4': message => "foo2 = ${my_module::foo2}", }
>>
>> }
>>
>> It's curious because I use no inheritance in my_module::internal,
>> no require etc. and despite that I can access to the variables of
>> my_module. Is it normal?
>>
>>
>
> It is very poor form to access $my_module::params::foo1 and
> $my_module::params::foo2 via inheriting class my_module (i.e. as
> ${my_module::foo1}, etc.).

But the my_module::internal inherits no class, so doesn't inherit
my_module class? I don't see where is the inheritance in the code
above.

Or, if I understand well, when I have:

class my_module {
include my_module::internal
}

I can say: "my_module::internal" inherits my_module. Is that correct?


> With that out of the way, it is better to say
> that the result you observe is among the valid possibilities than to say it
> is "normal".
>
> All class variables have global visibility regardless of inheritance or
> declaration (e.g. via include / require).

Do you mean that with:

class my_module {
include my_module::internal
}

In the body of my_module::internal class I can access to the variables
defined in my_module class? With a unqualified name?

> The main purpose of all that *as
> it pertains to variables* is to ensure that Puppet evaluates classes during
> catalog compilation in an order that assigns values to those variables
> before those values are used. It also serves a documentary purpose,
> showing clearly that one class has a dependency on another.
>
>
>
>> One thing very curious too: in the my_module::internal class
>> I can access to $var1 with this syntax :
>>
>> ${my_module::var1}
>>
>
>
> No, you can't. There is no $var1 in class my_module::internal, so you are
> not accessing such a variable. You are instead accessing
> $my_module::params::var1 via an alias (that alias resulting from class
> my_module inheriting my_module::params). Don't use that alias, even in
> class my_module.

Ok.

>> and $var1 is empty. Ok, I think I understand that. But in my
>> template "test_internal.txt.erb" I can use directly:
>>
>> blabla <%= @var1 %> blabla
>>
>> without the fully qualified name. Here, I don't understand.
>>
>
>
> I don't believe you.

I can give you a precise and quick (if you have already a Debian Wheezy)
process to reproduct this. I take a Debian Wheezy, updated and cleaned
(it's a simple VM in Virtualbox). And here is the process:

-------------------------------------------------------------------
# Quick install of puppetmaster
root@wheezy-clean:~# wget https://apt.puppetlabs.com/puppetlabs-release-$(lsb_release -sc).deb
root@wheezy-clean:~# dpkg -i puppetlabs-release-$(lsb_release -sc).deb
root@wheezy-clean:~# apt-get update
root@wheezy-clean:~# apt-get install puppetmaster

# I use puppet version 3.7.1
root@wheezy-clean:~# puppet -V
3.7.1

# In [main] section, I just:
# 1. comment this line -> #templatedir=$confdir/templates (deprecated)
# 2 add this line -> environmentpath = $confdir/environments
root@wheezy-clean:~# vim /etc/puppet/puppet.conf

root@wheezy-clean:~# cd /etc/puppet/environments/
root@wheezy-clean:/etc/puppet/environments# ls -l
total 4
drwxr-xr-x 4 root root 4096 Sep 18 04:35 example_env

# I create my conf (modules etc. in "production" directory)
# It's very basic. You can see my conf here http://sisco.laf.free.fr/divers/production.tar.gz
root@wheezy-clean:/etc/puppet/environments# wget http://sisco.laf.free.fr/divers/production.tar.gz
root@wheezy-clean:/etc/puppet/environments# tar -xf production.tar.gz
root@wheezy-clean:/etc/puppet/environments# rm production.tar.gz
root@wheezy-clean:/etc/puppet/environments# ls -l
total 8
drwxr-xr-x 4 root root 4096 Sep 18 04:35 example_env
drwxr-xr-x 4 1000 1000 4096 Sep 18 03:43 production

root@wheezy-clean:/etc/puppet/environments# apt-get install tree

# In ./manisfests/site.pp you'll can see that I just include
# the profile::test class.
# In ./modules/profiles/manifests/test.pp, I just declare
# the ::test class with one parameter
# And I think the "test" module will very easy to understand.
root@wheezy-clean:/etc/puppet/environments# tree production/
production/
├── manifests
│ └── site.pp
└── modules
├── profiles
│ └── manifests
│ └── test.pp
└── test
├── manifests
│ ├── init.pp
│ ├── internal.pp
│ └── params.pp
└── templates
├── internal.txt.erb
└── test.txt.erb

7 directories, 7 files

# I just add this line -> "192.168.0.115 puppet"
# where 192.168.0.115 is the IP address of my puppermaster
# I want to resolve the "puppet" short name.
root@wheezy-clean:/etc/puppet/environments# vim /etc/hosts

# I restart puppetmaster and run the agent.
root@wheezy-clean:/etc/puppet/environments# service puppetmaster restart
root@wheezy-clean:/etc/puppet/environments# puppet agent --test
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for wheezy-clean.chezmoi.priv
Info: Applying configuration version '1411008116'
Notice: module test class test -> foo1 = value_foo1
Notice: /Stage[main]/Test/Notify[n3]/message: defined 'message' as 'module test class test -> foo1 = value_foo1'
Notice: /Stage[main]/Test/File[/tmp/test.txt]/ensure: defined content as '{md5}f424081c82df8216c6085b29cedf3700'
Notice: module test class test -> foo2 = value_foo2
Notice: /Stage[main]/Test/Notify[n4]/message: defined 'message' as 'module test class test -> foo2 = value_foo2'
Notice: module test class test -> var2 = default_value_var2
Notice: /Stage[main]/Test/Notify[n2]/message: defined 'message' as 'module test class test -> var2 = default_value_var2'
Notice: module test class test::internal -> var2 = default_value_var2
Notice: /Stage[main]/Test::Internal/Notify[sn2]/message: defined 'message' as 'module test class test::internal -> var2 = default_value_var2'
Notice: /Stage[main]/Test::Internal/File[/tmp/internal.txt]/ensure: defined content as '{md5}77dc0e02e3caa93db00c87d29330ce92'
Notice: module test class test::internal -> var1 = specific_value_var1
Notice: /Stage[main]/Test::Internal/Notify[sn1]/message: defined 'message' as 'module test class test::internal -> var1 = specific_value_var1'
Notice: module test class test::internal -> foo1 = value_foo1
Notice: /Stage[main]/Test::Internal/Notify[sn3]/message: defined 'message' as 'module test class test::internal -> foo1 = value_foo1'
Notice: module test class test::internal -> foo2 = value_foo2
Notice: /Stage[main]/Test::Internal/Notify[sn4]/message: defined 'message' as 'module test class test::internal -> foo2 = value_foo2'
Notice: module test class test -> var1 = specific_value_var1
Notice: /Stage[main]/Test/Notify[n1]/message: defined 'message' as 'module test class test -> var1 = specific_value_var1'
Info: Creating state file /var/lib/puppet/state/state.yaml
Notice: Finished catalog run in 0.06 seconds

# And here is the result of my template.
root@wheezy-clean:/etc/puppet/environments# cat /tmp/internal.txt
# Class test::internal
var1 = specific_value_var1 <---------- Here!!!
var2 = default_value_var2
foo1 = value_foo1
foo2 = value_foo2

root@wheezy-clean:/etc/puppet/environments# cat /tmp/test.txt
# Class test
var1 = specific_value_var1
var2 = default_value_var2
foo1 = value_foo1
foo2 = value_foo2
-------------------------------------------------------------------

I have:

var1 = specific_value_var1

in the /tmp/internal.txt file but in my template internal.txt.erb
I use :

var1 = <%= @var1 %>

Did you have the same result? I don't understand and for me it's
not logical.

Another curious thing: even if I put:

$var1 = "internal"

in the test::internal class, the /tmp/test.txt don't change.

> That form of reference would work of class
> my_module::internal were inheriting my_module::params (or class my_module)
> because of the aliasing mechanism I described. It would also work if
> my_module::internal had a parameter or an ordinary variable of its own
> named "var1". It will not work with the code you actually presented.

I understand. But in this case, where is my error in the
commands above?

I will continue to think about your long answers (thank you again
for the help). For starters, I would like to known where I'm
wrong in the example above. Maybe I'm very tired and I missed something
simple and stupid.


François Lafont


jcbollinger

unread,
Sep 18, 2014, 10:40:20 AM9/18/14
to puppet...@googlegroups.com


Sorry, I worded that very poorly.

Class my_module inherits my_module::params.  Absent my_module declaring its own local variables $foo1 and $foo2, the inheritance results in variables $::my_module::params::foo1 and $::my_module::params::foo2 being accessible unqualified in class my_module's namespace.  Because those variables are accessible unqualified in class my_module, they are also accessible elsewhere qualified by namespace my_module (i.e. $::my_module::foo1) -- that little bit of evil should not be used.  Instead, always qualify variable references with the namespace in which the variable is actually declared (my_module::params in this case).

 
Or, if I understand well, when I have:

class my_module {
    include my_module::internal
}

I can say: "my_module::internal" inherits my_module. Is that correct?



No, not at all.  My apologies for the confusion.

 

> With that out of the way, it is better to say
> that the result you observe is among the valid possibilities than to say it
> is "normal".
>
> All class variables have global visibility regardless of inheritance or
> declaration (e.g. via include / require).  

Do you mean that with:

class my_module {
    include my_module::internal
}

In the body of my_module::internal class I can access to the variables
defined in my_module class? With a unqualified name?


No, not in the slightest.  I mean that if class my_module::params has a local variable with unqualified name "foo1", then that variable can be referenced from any class in any module as $my_module::params::foo1, regardless of any inheritance or include / require / contain statements anywhere.  Under some inheritance-related circumstances it can also be referenced in certain places via its unqualified name, but as a matter of form, it should be accessed only via its qualified name anywhere outside class my_module::params.

The main role of inheritance and the only role of include / require / contain with respect to class variables is as I described before:


> The main purpose of all that *as
> it pertains to variables* is to ensure that Puppet evaluates classes during
> catalog compilation in an order that assigns values to those variables
> before those values are used.  It also serves a documentary purpose,
> showing clearly that one class has a dependency on another.
>
>  
>
>> One thing very curious too: in the my_module::internal class
>> I can access to $var1 with this syntax :
>>
>>     ${my_module::var1}
>>
>
>
> No, you can't.  There is no $var1 in class my_module::internal, so you are
> not accessing such a variable.  You are instead accessing
> $my_module::params::var1 via an alias (that alias resulting from class
> my_module inheriting my_module::params).  Don't use that alias, even in
> class my_module.

Ok.


No, not ok, my apologies.  There is after all a $my_module:var1 -- it is a class parameter.  I overlooked that this is a different case from the $my_module::foo1 case discussed above.  So, it's right and fine to refer to $my_module::var1 if that's what you actually mean.

 

>> and $var1 is empty. Ok, I think I understand that. But in my
>> template "test_internal.txt.erb" I can use directly:
>>
>>     blabla <%= @var1 %> blabla
>>
>> without the fully qualified name. Here, I don't understand.
>>
>
>
> I don't believe you.

I can give you a precise and quick (if you have already a Debian Wheezy)
process to reproduct this. I take a Debian Wheezy, updated and cleaned
(it's a simple VM in Virtualbox). And here is the process:



I'm not prepared to set up and run this test, but if it genuinely produces the results you describe then that demonstrates a bug.  Here are the docs on Puppet scope: https://docs.puppetlabs.com/puppet/3/reference/lang_scope.html.  Here are the docs on referencing variables from templates: https://docs.puppetlabs.com/guides/templating.html#referencing-variables.  Inasmuch as this is one of the more heavily used areas of Puppet, I suspect that something else is going on in your environment, but if such a bug has indeed cropped up then I'm sure PuppetLabs would appreciate a bug report.

 
# And here is the result of my template.
root@wheezy-clean:/etc/puppet/environments# cat /tmp/internal.txt
# Class test::internal
var1 = specific_value_var1    <---------- Here!!!
var2 = default_value_var2
foo1 = value_foo1
foo2 = value_foo2

root@wheezy-clean:/etc/puppet/environments# cat /tmp/test.txt
# Class test
var1 = specific_value_var1
var2 = default_value_var2
foo1 = value_foo1
foo2 = value_foo2
-------------------------------------------------------------------

I have:

   var1 = specific_value_var1

in the /tmp/internal.txt file but in my template internal.txt.erb
I use :

    var1 = <%= @var1 %>

Did you have the same result? I don't understand and for me it's
not logical.

Another curious thing: even if I put:

    $var1 = "internal"

in the test::internal class, the /tmp/test.txt don't change.



Puppet does perform some caching that could perhaps explain a (short-lived) failure to track such a change.  You can restart the master process to forcibly flush all caches, and you can configure the cache timeout to zero to disable caching (not effective for master processes that are already running).  If your result still does not reflect the local $var1 then I think that's a pretty good sign that what's happening is not what you think.

 
> That form of reference would work of class
> my_module::internal were inheriting my_module::params (or class my_module)
> because of the aliasing mechanism I described.  It would also work if
> my_module::internal had a parameter or an ordinary variable of its own
> named "var1".  It will not work with the code you actually presented.

I understand. But in this case, where is my error in the
commands above?



I cannot explain the template result you described.  As I said, it might reflect a new Puppet bug, but I think it's more likely that for some reason the manifests you are looking at are not the ones from which your machine is being configured.  That might be a caching issue, or it might arise from something else specific to your environment.

 
I will continue to think about your long answers (thank you again
for the help). For starters, I would like to known where I'm
wrong in the example above. Maybe I'm very tired and I missed something
simple and stupid.



For what it's worth, you are asking good questions and performing appropriate experiments.  When something I'm trying looks right but doesn't work, the problem is almost always something simple (and sometimes stupid).  We all have those moments.


John

Francois Lafont

unread,
Sep 18, 2014, 6:59:56 PM9/18/14
to puppet...@googlegroups.com
Hi,

Le 18/09/2014 16:40, jcbollinger a écrit :

> Sorry, I worded that very poorly.

Really no problem. ;)

> Class my_module inherits my_module::params. Absent my_module declaring its
> own local variables $foo1 and $foo2, the inheritance results in variables
> $::my_module::params::foo1 and $::my_module::params::foo2 being accessible
> unqualified in class my_module's namespace. Because those variables are
> accessible unqualified in class my_module, they are also accessible
> elsewhere qualified by namespace my_module (i.e. $::my_module::foo1) --
> *that* little bit of evil should not be used. Instead, always qualify
> variable references with the namespace in which the variable is actually
> declared (my_module::params in this case).

Ok. Indeed I have read in Puppet documentation that it's better to avoid
unqualified variable (I have seen it's a "wolf trap") and it's better to
use fully qualified variable.

So, finally, is this way below a correct and good way to use the "params"
pattern?

-----------------------------------------------------
# Example of declaration for the "test" class.
class { test:
var1 => 'specific_var1',
}

#### test module with its classes ####
class test::params {
# API
$var1 = 'default_var1'
$var2 = 'default_var2'

# Shared variables
$foo1 = 'value_foo1'
$foo2 = 'value_foo2'
}

# The main class of the module

# I use fully qualified variable names exclusively!
class test (
$var1 = $::test::params::var1,
$var2 = $::test::params::var2,
) inherits test::params {

include ::test::internal

notify { 'v1': message => "var1 = $::test::var1", }
notify { 'v2': message => "var2 = $::test::var2", }
notify { 'f1': message => "foo1 = $::test::foo1", }
notify { 'f2': message => "foo2 = $::test::foo2", }

}

class test::internal {

notify { 'iv1': message => "Internal var1 = $::test::var1", }
notify { 'iv2': message => "Internal var2 = $::test::var2", }
notify { 'if1': message => "Internal foo1 = $::test::foo1", }
notify { 'if2': message => "Internal foo2 = $::test::foo2", }

# So, in templates, I use the syntax scope['::test::var']
# (I'm using Puppet 3.7.1 so -> scope[...])
file { '/temp/internal.txt':
content => inline_template("var1=<%= scope['::test::var1'] %>\nfoo2=<%= scope['::test::foo2'] %>\n"),
}
}
-----------------------------------------------------

I use $::test::xxx in class::internal not $::test::params::xxx
because when xxx is "var1" or "var2", I want to have the value of
var1 and var2 given as parameters of the test class (I don't want
the default value of var1 and var2).

Like this, it all seems to you correct?

> No, not in the slightest. I mean that if class my_module::params has a
> local variable with unqualified name "foo1", then that variable can be
> referenced from any class in any module as $my_module::params::foo1,
> regardless of any inheritance or include / require / contain statements
> anywhere.

Hum... there is exception, isn't? For instance, it depends of the
order of the class in the catalog, I'm wrong?

For instance, with this manifest:

-----------------------------------------------------
include ::test1
include ::test2

class test1 {
$var1 = "var1"
notify { 'm1': message => "var2 = $::test2::var2", }
}

class test2 {
$var2 = "var2"
notify { 'm2': message => "var1 = $::test1::var1", }
}
-----------------------------------------------------

I have this result:

~$ puppet apply manifest.pp
Warning: Scope(Class[Test1]): Could not look up qualified variable '::test2::var2'; class ::test2 has not been evaluated
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.02 seconds
Notice: var1 = var1
Notice: /Stage[main]/Test2/Notify[m2]/message: defined 'message' as 'var1 = var1'
Notice: var2 =
Notice: /Stage[main]/Test1/Notify[m1]/message: defined 'message' as 'var2 = '
Notice: Finished catalog run in 0.41 seconds

>> I can give you a precise and quick (if you have already a Debian Wheezy)
>> process to reproduct this. I take a Debian Wheezy, updated and cleaned
>> (it's a simple VM in Virtualbox). And here is the process:
>>
>>
>
> I'm not prepared to set up and run this test,

Ok. Let me show you an even faster test than the previous test (no installation
of puppetmaster just the puppet agent) :

root@wheezy-clean:~# lsb_release -sc
wheezy

root@wheezy-clean:~# wget https://apt.puppetlabs.com/puppetlabs-release-$(lsb_release -sc).deb
root@wheezy-clean:~# dpkg -i puppetlabs-release-$(lsb_release -sc).deb
root@wheezy-clean:~# apt-get update
root@wheezy-clean:~# apt-get install puppet
root@wheezy-clean:~# puppet -V
3.7.1

# Just comment the "templatedir" line in puppet.conf (which is deprecated)
root@wheezy-clean:~# sed -i -r 's/^(templatedir.*)$/#\1/g' /etc/puppet/puppet.conf

# Create a simple manifest.
root@wheezy-clean:~# vim manifest.pp

Here is my (very) simple manifest:

------------------------------
include ::test

class test {
$var1 = "value_var1" # LINE A
include ::test::internal # LINE B
}

class test::internal {
notify { 'n1': message => "var1 = $var1", }

file { '/tmp/internal.txt':
content => inline_template("var1 = <%= @var1 %>\n"),
}
}
------------------------------

# Apply the manifest.
root@wheezy-clean:~# puppet apply manifest.pp
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.06 seconds
Notice: /Stage[main]/Test::Internal/File[/tmp/internal.txt]/ensure: defined content as '{md5}90e1f6ab3dc65719105f56f3b58dee5d'
Notice: var1 =
Notice: /Stage[main]/Test::Internal/Notify[n1]/message: defined 'message' as 'var1 = '
Notice: Finished catalog run in 0.15 seconds

root@wheezy-clean:~# cat /tmp/internal.txt
var1 = value_var1

As you can see, in the local scope of test::internal, there is
no defined "var1" (and notify prints "var1 =") but in the
template, @var1 has been replaced by "value_var1".

And now, if I just swap LINE A with LINE B, I have this:

root@wheezy-clean:~# puppet apply manifest.pp
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.06 seconds
Notice: /Stage[main]/Test::Internal/File[/tmp/internal.txt]/content: content changed '{md5}90e1f6ab3dc65719105f56f3b58dee5d' to '{md5}62d0ae15ddb3f27d78aa9a04c7c9cc4d'
Notice: var1 =
Notice: /Stage[main]/Test::Internal/Notify[n1]/message: defined 'message' as 'var1 = '
Notice: Finished catalog run in 0.03 seconds

root@wheezy-clean:~# cat /tmp/internal.txt
var1 =

Honestly, I don't understand. Is it normal, is it a bug... I don't know.

And if I try this (just fully qualified names):

------------------------------
include ::test

class test {
$var1 = "value_var1" # LINE A
include ::test::internal # LINE B
}

class test::internal {
notify { 'n1': message => "var1 = $::test::var1", }

file { '/tmp/internal.txt':
content => inline_template("var1 = <%= scope['::test::var1'] %>\n"),
}
}
------------------------------

I have:

root@wheezy-clean:~# puppet apply manifest.pp
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.06 seconds
Notice: /Stage[main]/Test::Internal/File[/tmp/internal.txt]/content: content changed '{md5}62d0ae15ddb3f27d78aa9a04c7c9cc4d' to '{md5}90e1f6ab3dc65719105f56f3b58dee5d'
Notice: var1 = value_var1
Notice: /Stage[main]/Test::Internal/Notify[n1]/message: defined 'message' as 'var1 = value_var1'
Notice: Finished catalog run in 0.08 seconds

root@wheezy-clean:~# cat /tmp/internal.txt
var1 = value_var1

In this case, I'm ok (cool). But if I swap again LINE A and LINE B, I have:

root@wheezy-clean:~# puppet apply manifest.pp
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.06 seconds
Notice: /Stage[main]/Test::Internal/File[/tmp/internal.txt]/content: content changed '{md5}90e1f6ab3dc65719105f56f3b58dee5d' to '{md5}62d0ae15ddb3f27d78aa9a04c7c9cc4d'
Notice: var1 =
Notice: /Stage[main]/Test::Internal/Notify[n1]/message: defined 'message' as 'var1 = '
Notice: Finished catalog run in 0.03 seconds

root@wheezy-clean:~# cat /tmp/internal.txt
var =

And in this case, I don't understand. I thought that :

$var1 = "value_var1" # LINE A
include ::test::internal # LINE B

and

include ::test::internal # LINE B
$var1 = "value_var1" # LINE A

was completely equivalent. Am I wrong?

I will probably make au bug report but perhaps is it more sure to
wait if somebody has the same behaviour as me...

Thanks for your help.
François Lafont

jcbollinger

unread,
Sep 19, 2014, 11:36:36 AM9/19/14
to puppet...@googlegroups.com


The main points I am trying to get across are these:
  1. References to non-local variables should always be performed via qualified names, even when class inheritance makes the variable in question accessible also via its unqualified name.
  2. All qualified names used should accurately reflect the location of the referenced entity's declaration (some variables have multiple, distinct qualified names).
  3. If a class or defined type refers to a variable belonging to a different class then it should ensure via 'include' / 'require' / 'contain' or via class inheritance that the variable is initialized first.
I am not saying that references to variables declared locally should use qualified names, but if you want to adopt that practice then it's fine.

With respect to other classes' variables, though, I seem not to be successfully communicating point (2).  There are two formulations for your class test that I would consider good form, neither one exactly what you presented above.

Alternative 1:
----
class test (
  $var1 = $::test::params::var1,
  $var2 = $::test::params::var2,
) inherits test::params {

  include ::test::internal

  notify { 'v1': message => "var1 = $var1", }
  notify { 'v2': message => "var2 = $var2", }
  notify { 'f1': message => "foo1 = $::test::params::foo1", }
  notify { 'f2': message => "foo2 = $::test::params::foo2", }
}

----

Alternative 2:
----

class test (
  $var1 = $::test::params::var1,
  $var2 = $::test::params::var2,
) inherits test::params {

  include ::test::internal

  notify { 'v1': message => "var1 = $
::test::var1", }
  notify { 'v2': message => "var2 = $
::test::var2", }
  notify { 'f1': message => "foo1 = $::test::params::foo1", }
  notify { 'f2': message => "foo2 = $::test::params::foo2", }
}

----

I would also be ok with using qualified but not absolute variable references of the form "$test::params::foo1", but the absolute versions you presented are slightly safer.

Either way, the only variables "foo1" and "foo2" in the module belong to class test::params.  All references to them should be relative to their declaring class (test::params), notwithstanding the fact that because of class inheritance, Puppet should still find them if you instead qualify them with namespace "test::".

 

class test::internal {

  notify { 'iv1': message => "Internal var1 = $::test::var1", }
  notify { 'iv2': message => "Internal var2 = $::test::var2", }
  notify { 'if1': message => "Internal foo1 = $::test::foo1", }
  notify { 'if2': message => "Internal foo2 = $::test::foo2", }



As above, references to variables foo1 and foo2 should be qualifed by namespace 'test::params', not namespace 'test'.

Furthermore, this class definition above does not ensure that variables $::test::var1, $::test::var2, $::test::params::foo1, and $::test::params::foo2 have been initialized.  If it is in fact declared only by class test then that will work out, because class test provides those assurances.  Generally speaking, it would be better form for class test::inner to take that into its own hands, as doing so would make the class both more flexible and more self-documenting.  In this particular case, however, that would involve class test::inner 'include'ing class test, and that's not sensible when class test is also including test::inner. 

So, that's mostly OK, with the caveats that references to foo1 and foo2 should be differently qualified, and that class test::inner is encumbered with usage restrictions.

 
  # So, in templates, I use the syntax scope['::test::var']
  # (I'm using Puppet 3.7.1 so -> scope[...])
  file { '/temp/internal.txt':
    content => inline_template("var1=<%= scope['::test::var1'] %>\nfoo2=<%= scope['::test::foo2'] %>\n"),
  }


Again, qualify references to foo2 more appropriately:

  file { '/temp/internal.txt':
    content => inline_template("var1=<%= scope['::test::var1'] %>\nfoo2=<%= scope['::test::params::foo2'] %>\n"),
  }

 
}
-----------------------------------------------------

I use $::test::xxx in class::internal not $::test::params::xxx
because when xxx is "var1" or "var2", I want to have the value of
var1 and var2 given as parameters of the test class (I don't want
the default value of var1 and var2).


Yes, that's fine for var1 and var2, but not for foo1 and foo2 which are not declared by class test.  I don't see any particular purpose for foo1 and foo2 to live in test::params anyway, however, if they are not to be used as class parameter defaults.  You could as easily put them directly in class test, and then continue to refer to them as you are now doing.

 
> No, not in the slightest.  I mean that if class my_module::params has a
> local variable with unqualified name "foo1", then that variable can be
> referenced from any class in any module as $my_module::params::foo1,
> regardless of any inheritance or include / require / contain statements
> anywhere.  

Hum... there is exception, isn't? For instance, it depends of the
order of the class in the catalog, I'm wrong?



It used to be that the value to which the variable reference resolves depends on the order in which classes are evaluated (it would be nil if the declaring class is not evaluated before the variable reference), but the reference itself would be accepted without any kind of error, regardless of evaluation order.  Puppet's evaluator is getting smarter, but that doesn't invalidate my claim.

If the class evaluation order -- which is often difficult to predict -- happens to be such that no class variable is referenced before its class is evaluated, then all will be well.  That can happen without any class inheritance or any use of 'include' / 'require' / 'contain', as I said.  My point is that you should use 'include' / 'require' / 'contain', or occasionally class inheritance, to ensure a suitable class evaluation order, and that that is those statements' primary purpose as it pertains to variables.

 
For instance, with this manifest:

-----------------------------------------------------
include ::test1
include ::test2

class test1 {
  $var1 = "var1"
  notify { 'm1': message => "var2 = $::test2::var2", }
}

class test2 {
  $var2 = "var2"
  notify { 'm2': message => "var1 = $::test1::var1", }
}
-----------------------------------------------------

I have this result:

~$ puppet apply manifest.pp
Warning: Scope(Class[Test1]): Could not look up qualified variable '::test2::var2'; class ::test2 has not been evaluated
Notice: Compiled catalog for wheezy-clean.chezmoi.priv in environment production in 0.02 seconds
Notice: var1 = var1
Notice: /Stage[main]/Test2/Notify[m2]/message: defined 'message' as 'var1 = var1'
Notice: var2 =
Notice: /Stage[main]/Test1/Notify[m1]/message: defined 'message' as 'var2 = '
Notice: Finished catalog run in 0.41 seconds



And the appropriate solution to that problem is putting "include 'test2'" at the beginning of the body of class test1.

By the way, don't be confused about the meaning of the 'include' statement, or its siblings 'require' and 'contain'.  'Include' says that the named class must be included in the catalog; it does NOT interpolate the named class's body in place of the include statement (i.e. it is not analogous to a C preprocessor #include directive).  A given class can be the subject of any number of 'include', 'require', and 'contain' statements; no class will appear in the catalog more than once.

 


In part, it looks like you are running into bug 1220.  That was reported fixed in Puppet 3.5.0, but if I understand correctly, you need to enable the future parser to actually activate the bug fix (which would make the variable reference in your template fail to resolve in both cases).

 
And in this case, I don't understand. I thought that :

  $var1 = "value_var1"       # LINE A
  include ::test::internal   # LINE B

and

  include ::test::internal   # LINE B
  $var1 = "value_var1"       # LINE A

was completely equivalent. Am I wrong?



Yes, you are wrong, but the reason is a bit subtle.  For a comprehensive explanation, see http://puppet-on-the-edge.blogspot.com/2014/04/getting-your-puppet-ducks-in-row.html.

The short(ish) answer is that although the catalog Puppet constructs based on your manifests is declarative and order-insensitive, the process of evaluating your manifests to produce that catalog proceeds in and is subject to a well-defined (but difficult to predict) order.  In particular, when the evaluator encounters an 'include' statement naming a class that has not yet been evaluated, it immediately suspends evaluating the current class and evaluates the named class.  Statements later in the body of the original class will not yet have been evaluated during the evaluation of the included class.  This behavior is necessary and appropriate, as it allows, for instance, variables of the 'include'd class to be used in the definitions of variables of the including class.

More generally, your code is suffering from problems associated with bidirectional dependency.  It is fine for class test to declare class test::inner.  It is fine for class test::inner to refer to variables of class test (given some mechanism ensuring that class test will have been evaluated).  It is problematic -- but not altogether impossible -- to have both at the same time.  You really ought to consider externalizing your data and accessing via Hiera.  Though I'm not a big fan of class parameterization, you could also consider parameterizing class test::inner and using those parameters to give it the values it needs.


John

Francois Lafont

unread,
Sep 22, 2014, 7:01:19 AM9/22/14
to puppet...@googlegroups.com
Hi,

Le 19/09/2014 17:36, jcbollinger a écrit :

> The main points I am trying to get across

And I thnink that you have succeeded now. ;)

> are these:
>
> 1. References to *non-local* variables should always be performed via
> qualified names, even when class inheritance makes the variable in question
> accessible also via its unqualified name.
> 2. All qualified names used should accurately reflect the location of
> the referenced entity's declaration (some variables have multiple, distinct
> qualified names).
> 3. If a class or defined type refers to a variable belonging to a
> different class then it should ensure via 'include' / 'require' / 'contain'
> or via class inheritance that the variable is initialized first.

It's very clear. Thanks.

> I am not saying that references to variables declared *locally* should use
> qualified names, but if you want to adopt that practice then it's fine.
>
> With respect to other classes' variables, though, I seem not to be
> successfully communicating point (2).

Now I have understood the 3 rules.

>> I use $::test::xxx in class::internal not $::test::params::xxx
>> because when xxx is "var1" or "var2", I want to have the value of
>> var1 and var2 given as parameters of the test class (I don't want
>> the default value of var1 and var2).
>>
>
>
> Yes, that's fine for var1 and var2, but not for foo1 and foo2 which are not
> declared by class test. I don't see any particular purpose for foo1 and
> foo2 to live in test::params anyway, however, if they are not to be used as
> class parameter defaults. You could as easily put them directly in class
> test, and then continue to refer to them as you are now doing.

Yes, it's good idea.
Finally, as regards your comments, I think follow this pattern where I follow
your 3 rules, it seems ;):

-----------------------------------------------------
# Example of declaration for the "test" class.
class { test:
var1 => 'specific_var1',
}

#### test module with its classes ####

class test::params {

# API of the module with the default values.
$var1 = 'default_var1'
$var2 = 'default_var2'

}

# The *main* class of the module
class test (
$var1 = $::test::params::var1,
$var2 = $::test::params::var2,
) inherits test::params {

# Shared variables in all classes of the module.
$foo1 = 'value_foo1'
$foo2 = 'value_foo2'

include ::test::internal

# Here, I could use $var1, $var2, $foo1, $foo2
# (only in the main class of the module).
notify { 'v1': message => "var1 = $::test::var1", }
notify { 'v2': message => "var2 = $::test::var2", }
notify { 'f1': message => "foo1 = $::test::foo1", }
notify { 'f2': message => "foo2 = $::test::foo2", }

# etc.

}

class test::internal {

# It's an internal class of the module test which will be
# never called by the "user". So, the line below is useless.
# It's just for self-documenting.
# Maybe I could remove this line, I don't know...
require ::test

notify { 'iv1': message => "Internal var1 = $::test::var1", }
notify { 'iv2': message => "Internal var2 = $::test::var2", }
notify { 'if1': message => "Internal foo1 = $::test::foo1", }
notify { 'if2': message => "Internal foo2 = $::test::foo2", }

# So, in templates, I use the syntax scope['::test::var']
file { '/tmp/internal.txt':
content => inline_template("var1=<%= scope['::test::var1'] %>\nfoo2=<%= scope['::test::foo2'] %>\n"),
}
}
-----------------------------------------------------

> By the way, don't be confused about the meaning of the 'include' statement,
> or its siblings 'require' and 'contain'. 'Include' says that the named
> class must be included *in the catalog*; it does NOT interpolate the named
> class's body in place of the include statement (i.e. it is not analogous to
> a C preprocessor #include directive). A given class can be the subject of
> any number of 'include', 'require', and 'contain' statements; no class will
> appear in the catalog more than once.

Yes, it's clear and even clearer with your link below.

> In part, it looks like you are running into bug 1220
> <https://tickets.puppetlabs.com/browse/PUP-1220>.

Yes, this is exactly my problem!

> That was reported fixed
> in Puppet 3.5.0, but if I understand correctly, you need to enable the
> future parser to actually activate the bug fix (which would make the
> variable reference in your template fail to resolve in both cases).

I confirm. If I put:

parser = future

in the puppet.conf (in [main] section), I have the expected behaviour.
To my mind, the execpted behaviour (explained in the puppet documentation)
occurs with "parser = future" which is *not* enabled by default.
It's curious, but not a problem for me. I guess it's a backward
compatibility problem...

>> And in this case, I don't understand. I thought that :
>>
>> $var1 = "value_var1" # LINE A
>> include ::test::internal # LINE B
>>
>> and
>>
>> include ::test::internal # LINE B
>> $var1 = "value_var1" # LINE A
>>
>> was completely equivalent. Am I wrong?
>>
>>
>
> Yes, you are wrong, but the reason is a bit subtle. For a comprehensive
> explanation, see
> http://puppet-on-the-edge.blogspot.com/2014/04/getting-your-puppet-ducks-in-row.html

Thanks for this link which I read and read again. Very interesting.
This link has helped me to understand a lot of things in Puppet.
For intance, it's too simple to say "puppet is declarative".
No, this is not exact. There a phase, the evaluation of manifests
and the building of the catalog where puppet is deeply imperative.
Very interesting...

> both at the same time. You really ought to consider externalizing your
> data and accessing via Hiera.

I will ;) ... but not in module, only in "profiles" as explained in the
link below:

http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/

I don't want Hiera lookups in modules but only in profiles.

I have really learned a lot of things in this thread. Sincerely
thank you for all your long and detailed explanations. Although
I have cut lot of quotes of your messages, I have really read
everything very carefully.

Thank you again for your help. :)

François Lafont

Message has been deleted

David Danzilio

unread,
Sep 22, 2014, 9:16:12 AM9/22/14
to puppet...@googlegroups.com
What you've stumbled on is the concept of public and private classes. Public and private
classes are implemented very differently. A public class is your module's entry point,
this is how people interact with your module. A public class is where you define your
module's API. Private classes, on the other hand, are not meant to be called outside your
module. Private classes are the meat of your module. This is where you will actually
implement your module's functionality. A public class should be the only way a user can
interact with your private classes. Public classes should generally do very little. I
typically limit my public classes to validating the inputs received by my parameters and
calling my private classes. Since examples speak louder than words, here's an example:

# my_module/manifests/init.pp
class my_module (
  $var1 = $my_module::params::var1, 
  $var2 = $my_module::params::var2, 
  $var3 = $my_module::params::var3, 
) inherits my_module::params { 

  # Validate my parameters
  validate_string($var1)
  validate_array($var2)
  validate_bool($var3)

  # Call my 'private' classes
  class { 'my_module::install': } ->
  class {'my_module::config': } ~>
  class {'my_module::service'} ->
  Class['my_module']

}

The 'my_module' class is my public class. It has three parameters. All this class does is
validate my three parameters and call my private classes. Let's take a look at those
private classes:

# my_module/manifests/install.pp
class my_module::install (
  $var1 = $my_module::var1,
  $var2 = $my_module::var2,
) {

  private()

  # Body of the class

  ...

}

# my_module/manifests/config.pp
class my_module::config (
  $var2 = $my_module::var2,
  $var3 = $my_module::var3,
) {

  private()

  # Body of the class

  ...

}

# my_module/manifests/service.pp
class my_module::service (
  $var1 = $my_module::var1,
  $var2 = $my_module::var2,
  $var3 = $my_module::var3,
) {

  private()
  
  # Body of the class

  ...

}


As you can see, I've marked my private classes with the private function. This function
has not yet been released in stdlib, but is available in trunk
to implement this functionality in the DSL, if you prefer. The private classes concept
allows us to make some assumptions that affect the design of this module, specifically,
calling the variables in my_module without inheriting it first. We're assuming that, since
this is a private class, it will not be called independently of my_module, and those
variables will always be accessible.

This pattern can be found in a number of modules on the forge. Note, that these modules
don't use the private function since it hasn't been released yet. They assume that the
private classes are only being called inside their module. Here are some examples:


This is emerging very quickly as a good design pattern that fosters modularity,
simplicity, good separation of concerns, and clear data flow.

Francois Lafont

unread,
Sep 22, 2014, 11:29:51 AM9/22/14
to puppet...@googlegroups.com
Hi,

Le 22/09/2014 15:16, David Danzilio a écrit :

> What you've stumbled on is the concept of public and private classes.

Not exactly I think. The concept of public and private classes
was clear for me. It was the *implementation* of the "params"
pattern which was not clear for me, especially for private
classes. I think it's ok now. ;)

> Public and private classes are implemented very differently.
> A public class is your module's entry point, this is how
> people interact with your module. A public class is where you
> define your module's API. Private classes, on the other hand,
> are not meant to be called outside your module. Private classes
> are the meat of your module. This is where you will actually
> implement your module's functionality. A public class should
> be the only way a user can interact with your private classes.
> Public classes should generally do very little. I typically
> limit my public classes to validating the inputs received by
> my parameters and calling my private classes.

Totally agree with that. No problem.
Ok, it looks like what I gave in my previous message except:

1. the private() function. I didn't know it.

2. In my case, in a private class, I don't use parameters
and I use the variables of the public class directly with
the fully qualified name :

class my_module::service {
notify { "var1 = $::my_module::var1": }
# etc.
}

Thus, I don't have to write the parameters of the public class for *each*
private class. But, in return, I have to use the fully the qualified names
of these variables. ;)

> As you can see, I've marked my private classes with the private function.
> This function has not yet been released in stdlib, but is available in trunk
> (https://github.com/puppetlabs/puppetlabs-stdlib#private).

Thank you. Very interesting.

> Google can show you how to implement this functionality in the DSL,
> if you prefer. The private classes concept allows us to make some assumptions
> that affect the design of this module, specifically, calling the variables
> in my_module without inheriting it first. We're assuming that, since this is
> a private class, it will not be called independently of my_module, and those
> variables will always be accessible.
>
> This pattern can be found in a number of modules on the forge. Note, that
> these modules don't use the private function since it hasn't been released
> yet. They assume that the private classes are only being called inside their
> module. Here are some examples:
>
> https://github.com/garethr/garethr-docker
> https://github.com/puppetlabs/puppetlabs-mysql
> https://github.com/puppetlabs/puppetlabs-ntp
>
> This is emerging very quickly as a good design pattern that fosters
> modularity, simplicity, good separation of concerns, and clear data flow.

Ok, thanks for your remarks.

François Lafont

Felix Frank

unread,
Sep 22, 2014, 12:13:21 PM9/22/14
to puppet...@googlegroups.com
On 09/18/2014 04:40 PM, jcbollinger wrote:
>
> I'm not prepared to set up and run this test, but if it genuinely
> produces the results you describe then that demonstrates a bug. Here
> are the docs on Puppet scope:
> https://docs.puppetlabs.com/puppet/3/reference/lang_scope.html. Here
> are the docs on referencing variables from templates:
> https://docs.puppetlabs.com/guides/templating.html#referencing-variables.
> Inasmuch as this is one of the more heavily used areas of Puppet, I
> suspect that something else is going on in your environment, but if
> such a bug has indeed cropped up then I'm sure PuppetLabs would
> appreciate a bug report.

Aw man, that thread is way too voluminous for me to read in a spare
minute. But since you bring up templates - there was that bug, yes.

https://tickets.puppetlabs.com/browse/PUP-1220

It led to dynamic scoping rearing its ugly head again, when variables
were used in templates. Not sure if this is the issue here, but it
might. The OP can verify this by setting parser=future.

Cheers,
Felix

Francois Lafont

unread,
Sep 22, 2014, 12:19:50 PM9/22/14
to puppet...@googlegroups.com
Le 22/09/2014 18:12, Felix Frank a écrit :

> Aw man, that thread is way too voluminous for me to read in a spare
> minute.

Yes, sorry. It's a long thread but it's a finished thread I think. ;)

> But since you bring up templates - there was that bug, yes.
>
> https://tickets.puppetlabs.com/browse/PUP-1220
>
> It led to dynamic scoping rearing its ugly head again, when variables
> were used in templates. Not sure if this is the issue here, but it
> might. The OP can verify this by setting parser=future.

Yes, I have confirmed here: ;)

On Monday, September 22, 2014 4:01:19 AM UTC-7, François Lafont wrote:

> I confirm. If I put:
>
> parser = future
>
> in the puppet.conf (in [main] section), I have the expected behaviour.
> To my mind, the execpted behaviour (explained in the puppet documentation)
> occurs with "parser = future" which is *not* enabled by default.
> It's curious, but not a problem for me. I guess it's a backward
> compatibility problem...

François Lafont

Felix Frank

unread,
Sep 22, 2014, 12:27:19 PM9/22/14
to puppet...@googlegroups.com
On 09/22/2014 06:20 PM, Francois Lafont wrote:
>> But since you bring up templates - there was that bug, yes.
>> >
>> > https://tickets.puppetlabs.com/browse/PUP-1220
>> >
>> > It led to dynamic scoping rearing its ugly head again, when variables
>> > were used in templates. Not sure if this is the issue here, but it
>> > might. The OP can verify this by setting parser=future.
> Yes, I have confirmed here: ;)

\o/

Thanks
Reply all
Reply to author
Forward
0 new messages