On May 28, 2010, at 9:56 AM, Jonathan A. Booth wrote:
On 05/28/2010 05:00 AM, Luke Kanies wrote:
* Alessandro's presentation caused someone to point out to me
afterward that case statements of this ilk:
case $operatingsytem {
debian: { ... }
redhat: { ... }
}
make a module difficult to extend with support for new operating
system types - you have to modify the module itself in this case.
This situation is not so different from the same problem that occurs
with type/provider pairs in the ruby half of puppet. You just have
fancier tools to implement the case in the providers.
Intresting comparison.
Pulling the case out into extlookup() is nice for simple case
statements -- where you're only looking up the name of one package.
OpenSSH on Solaris vs RedHat vs Ubuntu it makes a great solution.
For puppet-dashboard on RedHat vs Ubuntu though, extlookup() isn't
as clear a winner.
Dashboard doesn't like Ubuntu's rake package (maybe that's a bug?
But ignore it for now for this example...) -- it wants the one from
rubygems. So now the package lists for rhel/ubuntu differ. Worse
still, there's an inter-list dependency where rake needs to be
installed via the gem provider, which requires rubygems installed.
Modeling that in extlookup() is going to be a worse mess than that
case statement -- if nothing else because now you also have to
extlookup() the provider=>. Plus potentially a requires=> for other
package situations -- the gem provider doesn't need it, but other
packages may need specific ordering to install %pre/%post scripts
correctly.
If there was a better way to specify a type/provider pattern in
puppet code (not ruby), that seems to me like a better solution
here. You can do this now, but it looks pretty hackish:
[warning: snippeted code. Don't expect it to work as presented here.
Original code can be provided if needed.]
class apache2 {
package { "apache2": alias => "apache2", ensure => present; }
...
# Include operating-specific aspects
include "apache2::${operatingsystem}"
}
define apache2::feature($purge = false, $ensure = 'present') {
search "apache2::${operatingsystem}"
provider::feature {"$name": purge=>"$purge", ensure=>"$ensure"; }
}
... later, in redhat.pp in the apache module:
class apache2::redhat inherits apache2 {
Package["apache2"] { name => "httpd" }
...
# Disable the default site
apache2::redhat::provider::site {"welcome.conf":ensure => absent;}
}
define apache2::redhat::provider::feature($purge = false, $ensure =
'present') {
$string = "LoadModule ${name}_module /u/l/a2/mod/mod_${name}.so"
$conf = "/etc/httpd/conf.d/features.conf"
case $ensure {
'present': { exec { "echo $string >> $conf": unless => "grep
'${string}' $conf"; } }
'absent': { exec { "sed -i '\...@^$string$@ d' $conf": onlyif
=> "grep '${string}' $conf"; } }
}
file {
"/etc/httpd/conf.d/features-${name}.conf":
source => "puppet:///modules/apache2/redhat/features-$
{name}.conf",
ensure => $ensure;
}
}
Of course rather than using ${operatingsystem} you could use an
externally-sourced ${apache2provider} selector. Then anyone could
extend the "default" apache2 module for their OS via their own
"class apache2::redhatfrommysite inherits apache2::redhat { ... }"
and use it by changing the external variable. On the down side,
they'd have to implement all the define ...::provider::<feature,
site, etc> even if they just re-called the parent provider's version
of it.
One bad thing about the above is until we get paramaterized classes
(assuming they can be multi-included!), you can end up running into
multiply-defined uglyness ("Apache2::Feature['foo'] already defined
at ...") you have to hack around with an if defined in the source
where you want to do the define.
... at least that's where I ended up on a new deployment I'm
building after considering the problem for a while. I'd rather keep
all my OS-specific configuration in one clearly defined location
than spread across multiple files in various locations. YMMV.
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.
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.
That doesn't start to approach your provider feature stuff, but I
think that's a bit outside my brain space right now. :)
--
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. --Nathaniel Borenstein
---------------------------------------------------------------------
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.