|
Here's a specific use case example of what today won't work and why it would would with defined ordering.
|
Puppet Classes
|
# Common interface class for a generic multi-tier web application consisting of
|
# a web frontend and a database backend.
|
class myapp (
|
$database_host,
|
$database_port,
|
) {
|
...
|
}
|
|
# Sub-component profile for a generic multi-tier web application; specifically,
|
# the database component. This class either includes or inherits myapp in order
|
# to make use of variables declared in the parent class, myapp.
|
class myapp::database (
|
$database_host = $app::database_host,
|
$database_port = $app::database_port,
|
) inherits myapp {
|
...
|
}
|
|
# Sub-component profile for a generic multi-tier web application; specifically,
|
# the web component. This class either includes or inherits myapp in order to
|
# make use of variables declared in the parent class, myapp.
|
class myapp::web (
|
$database_host = $app::database_host,
|
$database_port = $app::database_port,
|
) inherits myapp {
|
...
|
}
|
|
Classification for Database Node
|
{
|
"classes": {
|
"myapp": {
|
"database_host": "myapp.example.com",
|
"database_port": 5432
|
},
|
"myapp::database": {}
|
}
|
}
|
|
Classification for Web Node
|
{
|
"classes": {
|
"myapp": {
|
"database_host": "myapp.example.com",
|
"database_port": 5432
|
},
|
"myapp::web": {}
|
}
|
}
|
In this example, three classes are defined. All of them are parameterized. Two of them have parameter defaults and so don't require that the user declare them class-style, and in fact are written with the intention that their defaults will be correct in most circumstances and won't require manual intervention. However, their defaults are declared in another class.
The objective is to only have to set shared parameters once. This example shows a 2-component app. The components may be deployed to separate systems, or they may all be deployed to the same system for test, development, or small deployments.
|
Classification for Monolithic Node
|
{
|
"classes": {
|
"myapp": {
|
"database_host": "myapp.example.com",
|
"database_port": 5432
|
},
|
"myapp::database": {},
|
"myapp::web": {}
|
}
|
}
|
Per the docs, using inheritance this way is a commonly accepted pattern to help ensure evaluation order. However, as the parent class is itself parameterized, it isn't enough to just inherit; we actually have to make sure that that parent class is evaluated before the child.
As written today, however, if any of the above classifications is given to Puppet through an ENC, evaluation will fail.
Take the first classification example, for a Database node.
Evaluation will first partition classes specified with parameters from those specified without. Then it will evaluate classes without parameters, followed by those with parameters. Thus evaluation order will be:
-
myapp::database
-
myapp
This is not the order the classes were specified in by the classifier, and this will fail. Puppet will evaluate myapp::database, which inherits from myapp. Thus an instance of the myapp class will exist in the catalog after evaluation of myapp::database is complete. When evaluation then moves on to the next node class, myapp, Puppet will bail out with a duplicate class declaration error.
If we implement EITHER 100% honored ordering OR classes-with-parameters-first ordering, we resolve or mitigate the problem, respectively.
Proposed Behavior
If a class with parameters invokes by include or inheritance a class that is additionally specified by the classifier but WITHOUT parameters, no conflict will occur. include is singleton, and safe to invoke multiple times.
Current Behavior
If a class without parameters invokes by include or inheritance a class that is additionally specified by the classifier but WITH parameters, the second evaluation with parameters will be a duplicate class declaration error.
Hope that helps clarify this all a bit.
|