Hi all,

We at Reductive Labs keep running into clients who need something like  
an attribute class - that is, for a given module, they want a single  
class that handles all of the variable setting and overriding, and  
then they want that attribute class to be merged into all or some of  
the classes in the module.  (Teyo has especially been pushing me to  
solve this problem.)

This looks a lot like composition, which Puppet doesn't currently  
support - you include one class's behaviour in another, rather than  
inheriting.  This lack of composition is, I think, partially why  
people keep trying to do variable inheritance and getting frustrated  
that it doesn't work well.

While doing a code audit for a client this week, though, I found their  
solution to this problem to be close enough that I was able to take it  
almost the rest of the of way.  They had what amounts to a 'data'  
module with a class for each class that needed external data, and then  
they defined all of the attributes in that data class.  That is, if  
you have an 'apache' class, you'd also have a 'data::apache' class  
with a bunch of attributes.  Then all of your usage of those  
attributes would say '$data::apache::variable'.

The big benefit of this model for them is that it allows clean change  
control of all of the important configuration data (as opposed to  
manifest structure and resources), and, again, it looks a lot like  
composition.  In their case, they branch this module for all of their  
environments, but none of the other modules.

As soon as I saw it, though, I thought of a way that might make it  
better, so I wrote a function that enables that way (and a converter  
for their existing data).

Basically, I created (and have attached) a simple function that knows  
how to find and load a yaml file from a data directory, and it loads  
that file as a hash of parameters that should be set as local  
variables in the class.

For instance, say you have this class apache; you'd create this file:

data/apache.yaml

And put all of the attributes you care about in that file.

Then, in your apache class, you'd say:

class apache {
   load_data()
   ...
}

It would pick the right file based on the class name (although the  
current function allows you to specify the class, also), load it, and  
set all of the contained attributes as local variables in the class.   
So, if your apache.yaml file looks like this:

---
host: myhost.com
port: 80

Then this 'load_data' call is equivalent to this Puppet code:

class apache {
   $host = "myhost.com"
   $port = 80
}

The function is currently set up to support one big 'data' directory  
for all of your modules.  One could argue that it should instead  
support a data file per module, but the benefit of this one big data  
directory is that it makes it *much* easier to write sharable modules  
- you extract all of your site-specific data into this data dir, and  
you share the module with essentially no site data.  It's probably  
most reasonable to support an in-module data file and a site-wide data  
directory to make it easy to provide defaults.

I'm beginning to think that this, or a function a lot like this,  
should be included directly into Puppet, and data should get loaded  
automatically, rather than requiring the call to the 'load_data'  
function.

What do you think?

-- 
Fallacies do not cease to be fallacies because they become fashions.
     --G. K. Chesterton
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Users" group.
To post to this group, send email to puppet-users@googlegroups.com
To unsubscribe from this group, send email to 
puppet-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/puppet-users?hl=en
-~----------~----~----~----~------~----~------~--~---

# Copyright 2009 Reductive Labs
# Licensed under the GPLv2
Puppet::Parser::Functions.newfunction :load_data, :type => :statement do |args|
    if args.length == 1
        klass = args[0]
    else
        klass = resource.title
    end

    datadir = "/REPLACE/THIS/VALUE"

    dirs = datadir.split("/") + klass.split("::")
    name = dirs.pop + ".yaml"
    dir = dirs.join("/")
    path = File.join(dir, name)

    unless FileTest.exist?(path)
        raise ArgumentError, "Could not find data file for class %s" % klass
    end

    params = YAML.load_file(path)

    raise ArgumentError, "Data for %s is not a hash" % klass unless params.is_a?(Hash)

    params.each do |param, value|
        setvar(param, strinterp(value))
    end
end

Reply via email to