I love Puppet
I’m what you would call a newbie when it comes to puppet, I’ve be fortunate enough to work with it for the last 2 years but I only started using it 15 months back. I’ve used other configuration management tools in the past, and one day maybe I will bore you with the inner workings os RHN Satellite Server, but puppet was different.
For me it had great power over the RHN Satellite Server I had used, it allowed for me to have dynamic configuration that was structured and sensible, although the Satellite server allows you to replace certain variables it was nowhere near as powerful.
Luckily for me I had my own personal puppet guru in the form of Adrian Bridgett who has been working on puppet since the early days. It enabled for me to progress quite quickly through some of the less interesting features and straight into the world of classes and parameters.
As time progressed I learnt all about the different resources and various structures I could utilise, the various quirks of working with puppet, such as loops, if you ever need to do a loop, good luck. In short I have had the privilege of working with some very interesting puppet code created by people who knew puppet a lot better than I, all the time with a resource on hand to help me learn.
I hate duplication!
One of my pet peeves with puppet at the moment is the way you have to pass variables around to make things work and that proper inheritance of the classes is just so poor.
For example, The following is a simple set of classes that works with no issues.
init.pp
class frank ( $foo ="bar" ) { include frank::bob class { "frank::joe": foo => $foo } }
bob.pp
class frank::bob { file { "/var/lib/bob": ensure => "directory" } }
joe.pp
class frank::joe ($foo="bob") { file { "/var/lib/bob/${foo}.txt": content => template("module/file.txt"), owner => "frank", } }
Okay, so we have some class called frank, which takes a param $foo, includes a class bob and calls another class called joe and passes a param foo to it. This is a pretty standard way to pass variables around and it works flawlessly. The downside? It’s rubbish, on simple classes as Puppetlabs always shows you it’s fine you have a handful of parameters and it’s not too much hassle to maintain or they show you a very simple ntp class that doesn’t give you a feel for the more complicated modules you may be writing yourself. You can of course just reference the variable directly or not pass them into the child classes… Wrong If you are using these in templates you will end up having variables that are not in the scoped path for the template. You could reference the nicely scoped variable i.e. $frank::joe::foo this works as long you want the default or you can spend ages working out how to get the variables around, if you actually want to set the variable you need to call the class directly so you end up with a more complicated nodes manifest which isn’t the end of the world, but in the world of keep it simple less is more.
So imagine the above but where you want to pass in 25 or 40 parameters, believe it or not, we have some complicated configuration files and to help us keep the flexibility in the module as much as possible. You basically end up duplicating a lot of variables around, and your init.pp will be huge as a result.
It’s probably worth saying that we try to keep where possible the most simplest node manifest, so typically the largest node manifest would be maybe 10 lines.
So here’s a better way to achieve the same things with less configuration.
init.pp
class frank ( ) inherits frank::joe { include frank::bob }
bob.pp
class frank::bob { file { "/var/lib/bob": ensure => "directory" } }
joe.pp
class frank::joe ($foo="bob") { file { "/var/lib/bob/${foo}.txt": content => template("module/file.txt"), owner => "frank", } }
One simple change, inherit! it makes so much sense, the init.pp has has everything that is in the frank::joe and some more, wouldn’t it be nice if that worked. Well it Doesn’t thanks to this and a number of other bugs, the inheritance within puppet just isn’t powerful enough, and more importantly doesn’t work as expected.
It seems the only way to get around this is the first method, which means that even if you are splitting your classes out you end up with duplication and a more confusing modules.
I encourage everyone to give their own personal view-point on this as it’s an area I’d like to learn more about.
Summary
I want an easy life, I want puppet modules that allow for proper inheritance and dependency management, I don’t want to be working around quirks of an application, I want to work with the application and I want it to be simple.
I am not a puppet expert, but I do think that you should be able to pass parameters through to the inherited class without having the variables re-defined at every level, What do you think?
[…] few weeks ago I wrote an article which was more of a rant than necessary. I was trying to drastically alter the way we right one of our puppet modules, and […]
I have this same exact problem and wish this could be addressed by puppet.
You don’t detail the problems you were having with inheritance so I can’t speak to them except to say that Puppet Labs recommends only using inheritenace for very simple scoping tricks like in the “params pattern”. There’s almost always an easier and more transparent way to achieve the goal.
As to interaction between scoping and ERB, there’s the scope.lookupvar() function which can be used to allow injection of variables by walking the Puppet namespace even though ERB has no knowledge of Puppet namespacing syntax. See example here:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gistfile1.txt
hosted with ❤ by GitHub
Although I actually prefer explicit passing of params to child classes in nearly all cases, scope.lookupvar() would allow you to reference the parent class params from your children without adding additional Puppet DSL code. I suppose that is fine if the child classes are for internal use by your module only and are never meant to be declared directly by users of your module. If the classes might be declared directly by users of the module then I would recommend the additional DSL scaffolding of passing the params to the child classes as well. Just seems a cleaner interface to users of your module.
I will say that in the past Puppet Labs has perhaps not always done the best job of communicating what they’d consider “best practice” to the community but that has been more of a lack-of-cycles problem than anything else. As the company has grown we now have folks dedicated to doing just that. If you are looking for an easier-to-consume stream of that kind of information I’d recommend the Puppet Labs blog and Twitter feed as opposed to the high-traffic puppet-users mailing list:
http://puppetlabs.com/blog
http://twitter.com/puppetlabs
Hope that helps.
I think you posts do a better job explaining what the inheritance can do. The issue, and I have to try and remember a long way back now, was that the inheritance in puppet is not true inheritance. They have implemented a class and inheritance framework that doesn’t work as expected, I wouldn’t be surprised if this is why ERB struggles. Unfortunately I don’t have and can not recall any specific examples but i’ll try and be more specific in the future.
It’s true that ‘class’ and ‘inheritance’ in Puppet DSL are a little overloaded if you are coming from an OO programming background. In essence, classes and interitance in Puppet are nothing more than scoping tricks which falls far short of their def in OO.