On Jun 3, 2010, at 1:53 PM, Jonathan A. Booth wrote:

On 06/02/2010 07:42 PM, Luke Kanies wrote:
How would something like this work as a language feature?  Something
like:

class apache2::redhat implements apache2 for(operatingsystem =>
redhat) { ... }
>
We need something that can be queried from outside, and we need
something that can essentially be automatically loaded when the base
class is evaluated.

That could work for me. Agreed on the query-ability, though I'd still like to leave in a override mechanism. Example: so you could override apache2::redhat to use a custom apache package, rather than the generic redhat one -- yet otherwise keep the other standard "apache redhat module" behaviors intact -- how sites are defined in / etc/httpd/conf.d/, etc, etc.

I think this is a good bit more complicated, unfortunately - or rather, I don't know how you'd do it. Maybe you'd add further search paths to the implementing class? Like:

class apache2::redhat implements apache2 for(...) { }

class mystuff inherits apache2::redhat {}

Probably the best thing is to find a way to extend the autoload path for the base class. Or something?

But it's probably unnecessary to do in stage one. I think we're pretty far afield from the initial question (thus my long delay in responding).

It seems like we almost want to build an automatically included class
heirarchy, with a class search path that the base class can define:

class apache2
loads_subclasses(operatingsystem::operatingsystem_release,
operatingsystem) { ... }

So, when someone included apache2, Puppet would automatically look for
"apache::$operatingsystem::$operatingsystem_release" then "apache::
$operatingsystem", and include them if found.

Doing it in this way would mean folks could override apache2 with a child class, then override the load_subclasses line. That'd let them include their own partial or full implementations (which couldn't be named apache2::$operatingsystem since that'd be duplicate definitions), so I guess that'd work out fine. Bit complex and intimidating looking though...

Indeed. Ideally people would experiment with this in the pure ruby DSL, and only when it'd gotten a bit settled would we add it to the main language.

That doesn't start to approach your provider feature stuff, but I
think that's a bit outside my brain space right now. :)

It might be able to be much simpler; boiling it down, all we're really doing is writing a lot of fancy syntax to accomplish what looks a lot like a shell alias, right? Consider:

class apache {
 include apache::$os
 implements {
   "apache::featuers": provider => apache::$os::features;
   "apache::sites": provider => apache::$os::sites;
 }
}

which would essentially translate any class/define calls to apache::features into apache::$os::features. The only trick there is it'd be hard to override the "include apache::$os" -- at least until parameterized classes and you could pass in a "provider=>" to the apache class (defaulting to provider=>$operatingsystem). {post- thought: I suppose extlookup() actually would be a really good solution there until or even after param'd classes.}

2.6 will have parameterized classes (they're already done, just not released), but I don't really know how that helps you.

The problem with the above proposal is that none of it is queryable - it's only available by evaluating the class, which means it's not introspectable.

Really, I'd like to move most/all of the 'include' lines to static declarations:

class apache includes "apache::$os" {}

Or some equivalent - then, again, you can ask a class what its dependencies are.

This contradicts any type/providers pattern by removing the "provider knows where it belongs" pushing that up into the generic apache class. I'm not particular on where that applicability decision resides, but pushing it up keeps the DSL simpler and in many ways, clearer. Module developers specify "sane" defaults, but individual sysadmins running puppet can override them.

It'd be easy to use too -- add site-specific tweaks to a module -- so if I run RedHat but need to store things in /services for historic reasons, I can tweak the provided classes to do that with (relatively) minimal effort. I don't have to fork the apache module, nor do I have to start from scratch. Or be sick and wrong and mix and match providers, thus:

class siteapache inherits apache {
 # Use CAR instead (Custom Apache on Redhat)
 include siteapache::myca
 Implements["apache::features"] { provider=>apache::redhat::sites }
 Implements["apache::sites"] { provider=>apache::debian::sites }
 file { "/etc/httpd/conf.d/debian_style_sites.conf":
   content => "Include /etc/apache2/sites-enabled/" }
}
class siteapache::myca inherits apache::redhat {
 Package["httpd"] { name => "mycrazycustomapache" }
}


I can't tell from a brief grep around the puppet ruby code if implements{} would be easy to write or not. My offhand guess is fairly easy -- it'd basically just reach inside puppet's class listings and at the worst, subclass $name to provider=>value. (I did try 'class foo inherits foo::$operatingsystem' in the DSL itself, but the parser wasn't impressed with a variable interpolation post 'inherits'.)

Most of this functionality can be added to Puppet::Resource::Type, and can be experimented with a bit in the pure ruby dsl. That is, add the functionality (attributes, code, etc.) to Puppet::Resource::Type, and expose it in the internal dsl by adding support (where necessary) in Puppet::DSL::ResourceTypeAPI.

If you're willing to take a stabe at this, I'd definitely like to help you sort it out.

One big drawback I'm seeing is this is likely order-dependent. Until apache/implements{} is evaluated, calls to apache::sites are likely to be undefined. So you may never be able to 'include apache' -- you'll have to 'require apache' instead.

One of the benefits to having everything statically declared is that you're not reliant on order - you're basically building a graph of all of the classes and what goes into them (whether you explicitly do so or not) and just evaluating as necessary.

Anyway, as I said above, we've gotten pretty far afield from where this started. I think the solution for 2.6 is to include Ohad's variant of extlookup, probably naming the function 'lookup'. As much as I'd love to get it static, I don't think it's reasonable on the timeframe we're on.

--
The easiest way to figure the cost of living is to take your income and
add ten percent.
---------------------------------------------------------------------
Luke Kanies  -|-   http://puppetlabs.com   -|-   +1(615)594-8199

--
You received this message because you are subscribed to the Google Groups "Puppet 
Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to