Duplicate declaration - class is already declared

465 views
Skip to first unread message

Julian Meier

unread,
Sep 11, 2015, 5:31:40 PM9/11/15
to puppet...@googlegroups.com
Hi all

I’ve got three classes (example):

```
class mymodule (
$user = 'foobar',
) {}

class mymodule::classone (
$foo = 'bar',
) inherits ::mymodule::classone::params {
require mymodule
notify { "username: ${::mymodule::user}": }
}

class mymodule::classtwo (
$foo = 'bar',
) inherits ::mymodule::classtwo::params {
require mymodule
notify { "username: ${::mymodule::user}": }
}

class mymodule::classone::params {}
class mymodule::classtwo::params {}
```

My idea is to have a class `mymodule` with parameters used by any other class. If someone would like to change the default parameters of `mymodule` he can do it like (or with hiera and autolookup):
```
class { ::mymodule: user => 'foo', }
class { ::mymodule::classone: }
```
And for `classtone` and `classtwo` there is a specific `params.pp` (inherits)…

My problem: the declaration above can cause dependency cycles… If `classone` gets used before class `mymodule`. :-(

Do you have a better idea or design?
Thanks for any input and your help!
Julian

jcbollinger

unread,
Sep 14, 2015, 9:52:40 AM9/14/15
to Puppet Users


What you are observing is not a dependency cycle, it is an evaluation-order dependency.

 
Do you have a better idea or design?


Yes: never use a resource-like class declaration to declare any public class of any module.

A "public class" is one that is intended to be declared from outside the module, which appears to include at least classes ::Mymodule and ::Mymodule::Classone in your example.  Do not use a resource-like class declaration for such a class either outside its module or inside.  Instead use include-like declarations for such classes, and rely on automated data binding via Hiera to assign non-default class parameter values to them.

The particular problem in this case is that if the catalog builder evaluates a resource-like declaration of a given class, that declaration must be the very first declaration of the given class it sees.  The 'require mymodule' appearing in ::Mymodule::Classone and ::Mymodule::Classtwo is an include-like declaration of class ::Mymodule, and it is evaluated as part of evaluating the declaration of either of those classes.  When a resource-like declaration of the same class is encountered later, the catalog builder objects.

This constraint is basically about determining the applicable class parameter values: Puppet permits the same class to be declared multiple times, with the second and subsequent ones being no-ops.  Class parameter values must be determined at the first declaration.  And that's why you must avoid resource-like declarations of public classes -- it is very difficult to predict where another declaration of such a class may be (since it's public), or the relative order in which such a declaration may be evaluated.

As a secondary design consideration, it rarely makes sense to have one public class of a module that depends on another public class of the same module.  Many modules have only a single public class, and a few have multiple, independent public classes, but I can't name any off the top of my head that use the type of pattern you presented, with dependent public classes.  Those I can think of that have something like that provide defined resource types instead of dependent public classes.  Indeed, it may be that your Mymodule::Classone and Mymodule::Classtwo really want to be different instances of one defined type.  That's not going to get you around a class-declaration issue such as the one you presented, however.


John

Julian Meier

unread,
Sep 15, 2015, 3:33:16 PM9/15/15
to puppet...@googlegroups.com
Thanks you! The explanation about private and public classes is just great!

So, there seems to be no way to define IN the module how public classes get evaluated (order), right?
I really want to use some params from `::ospuppet` in `::ospuppet::master` and `::ospuppet::server` (https://github.com/juame/juame-ospuppet/tree/0.3.0).

Julian

--
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/3ab543f5-1b7b-412d-b942-1edf1f5d5c66%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

jcbollinger

unread,
Sep 16, 2015, 9:26:27 AM9/16/15
to Puppet Users


On Tuesday, September 15, 2015 at 2:33:16 PM UTC-5, Julian Meier wrote:
Thanks you! The explanation about private and public classes is just great!

So, there seems to be no way to define IN the module how public classes get evaluated (order), right?


Evaluation order is deterministic, as governed by the order of DSL statements in your manifests.  Manifests are evaluated in document order, left-to-right, top-to-bottom.  In that sense, yes, you can define in a module in what order classes are evaluated.  You cannot, however, use declarations in one manifest to influence the order of evaluation of declarations in a different manifest.  This is why the differentiation between public and private classes is so important.  For private classes, you can control evaluation order because you can control all the places where a declaration may be evaluated, but for public classes, all bets are off.


I really want to use some params from `::ospuppet` in `::ospuppet::master` and `::ospuppet::server` (https://github.com/juame/juame-ospuppet/tree/0.3.0).



That's fine.  Then rely on automated data binding to set those parameter values.  If class ::ospuppet serves only to house such shared data, then as a bonus, you will no longer need to declare it from outside the module at all.  In that case you could make it private, but if you do so then I would rename it to avoid confusion, maybe to ::ospuppet::data.

Or if you're going that far, then you could also consider getting rid of the shared data class altogether.  Your two other classes could instead obtain the data directly from Hiera, by calling one of its data-access functions.  You then obtain data sharing / deduplication by having each class that wants the data look it up in Hiera via the same key.


John

Reply all
Reply to author
Forward
0 new messages