The Puppet Labs Issue Tracker has Moved: https://tickets.puppetlabs.com

Feature #1645

Classes vs. Definitions - how to get closer to common design patterns

Added by Florian Grandel about 6 years ago. Updated about 3 years ago.

Status:Needs More InformationStart date:10/11/2008
Priority:NormalDue date:
Assignee:Nigel Kersten% Done:

0%

Category:language
Target version:-
Affected Puppet version:0.24.4 Branch:
Keywords:

We've Moved!

Ticket tracking is now hosted in JIRA: https://tickets.puppetlabs.com

This ticket may be automatically exported to the PUP project on JIRA using the button below:


Description

I am a newcomer to puppet. This is bad as I don’t know all the nuts and bolts of the framework. But it is good as well as I still have a “fresh view” of puppet as a tool. So please forgive me if I am telling nonsense … Anyhow I’d like to share some “fresh-view” conceptual thoughts with you.

I am really having conceptual (and practical!!) trouble with the semantics of puppet’s “classes” and “definitions” as they are defined today. IMO the distinction is quite arbitrary and counter-intuitive. AFAICS there are three main distinctions between classes and definitions:

1) classes are singletons from a node perspective, definitions are instantiable 2) definitions receive parameters, classes do not 3) classes can declare “class variables” ($class::var), definitions cannot

I have seen many examples in “recipes” where classes were used like this:

node abc {
  $globalvar1 = ...
  $globalvar2 = ...

  include xyz
}

class xyz {
   ...template("consumes <%= globalvar1 %>...")...

   file { ...owner = $globalvar2... }
}

IMO this is counter to basic principles of information hiding and modularization. (The most exhaustive discussion of the disadvantages of such an approach is probably in Steve McConnell’s “Code Complete”). If you do not have the most simple classes in your manifests then puppet invites you to use global variables. I can already see from the few things I did that this quickly becomes a maintenance nightmare and makes puppet classes very difficult to use in a more complex setting.

Definitions accept construction parameters and are therefore able to hide their inner workings and complexity from outer context. This makes them more flexible and attractive for modularization than classes. Definitions are however not meant for usage as node singletons. IMO it’s always bad if you try to coerce a syntactical construct into a role that it wasn’t meant for. Defintions also have another big disadvantage: They do not allow for instance variables like classes do (something like $mydefinition[“xyz”]::myvar).

Furtheron the distinction between “classes as singletons” and “definitions as prototypes” seems quite arbitrary to me. What is called a “class” in puppet is conceptually a class with “automatic instantiation” (with one “class instance” per node). This is confusing to me as an OO programmers who considers classes and instances separate conceptual entities. On the other hand what is called a “definition” in puppet is conceptually really a class as it defines a “blueprint” of an object that can be instantiated separately. The conceptual inconsistency reveals itself in the Class[“classname”] syntax which is a workaround for the fact that puppet classes are instances of classes as well. Definitions on the other hand can be referenced quite intuitively with the Definition_name[“instance id”] syntax.

IMO the conceptual distinction between classes and definitions is confusing and unnecessary at the same time. To me it would be much simpler if such a distinction did not exist. Rather than two resource containers you could simply have one generic class container without loosing any functionality and winning flexibility, intuitivity and maintainability:

Here my propositions: 1) Classes should be reframed as universal “blueprints” of resource containers that can (and must) be instantiated (like definitions today). 2) All classes may define constructor parameters (like definitions today). 3) Classes may be defined as node level “singletons” or node level “prototypes”. The distinction between singleton and prototype is well known to most OO programmers. 4) Classes can define public instance variables that may be accessed from an outer context (like they do today). The access syntax would be ($Classname[“instance id”]::myvar). 5) Instances of classes can be referenced like definitions today: Classname[“instance id”]

All this can be introduced in a fully backwards compatible way (which is certainly a must-have!): 1) Definitions will continue to exist but will be deprecated. 2) Classes will be node singletons by default. 3) Class constructor parameter declaration is optional. 4) Singleton classes can still be referenced as Class[“xyz”]. This usage will however be deprecated. 5) Class variables can still be referenced as $classname::var. This usage will however be deprecated. 6) You can define “inner classes” to replace today’s construct of definitions within class context.

Here is how I’d define the syntax of the ideal resource container (replacing both classes and definitions), not really BNF but somehow ;–) :

{singleton|prototype} class  [($par1, [$par2, [...]])] {
    ...
    $instance-var = ...
    ...
    {singleton|prototype} class  [($par1, [$par2, [...]])] { ... }
}

The whole thing certainly is not very well thought out yet. But I’d love to see a discussion starting from here as I am really fighting with the current syntax…

History

#1 Updated by Florian Grandel about 6 years ago

My “bad use” example was in conflict with Redmine wiki syntax. Preview somehow doesn’t work in my browser. Here it is again:

node abc {
  $globalvar1 = ...
  $globalvar2 = ...

  include xyz
}

class xyz {
  ...
  template("...consumes <%= globalvar1 %>...")
  ...
  file { ...owner = $globalvar2... }
}

#2 Updated by James Turnbull about 6 years ago

  • Category set to language
  • Status changed from Unreviewed to Needs Decision
  • Assignee set to Luke Kanies
  • Target version set to 4
  • 3 changed from Unknown to Hard

#3 Updated by Luke Kanies almost 6 years ago

I’ve taken this discussion to the dev list; it’s much more amenable to discussion.

#4 Updated by Luke Kanies almost 4 years ago

  • Assignee changed from Luke Kanies to Nigel Kersten

#5 Updated by Nigel Kersten almost 4 years ago

  • Status changed from Needs Decision to Needs More Information

Florian, I’m not sure if you’re still watching Puppet at all, but have you investigated our parameterized classes work?

I understand this isn’t addressing all the concerns here, but it goes a long way I believe.

#6 Updated by James Turnbull about 3 years ago

  • Target version deleted (4)

Also available in: Atom PDF