We surprisingly haven't had much discussion of the basic function of
modules since we created them about two years ago, but I'm looking for
design help on them now.
Somewhat on a lark, and because I think it could be really powerful if
we do it right, I'm taking a crack at getting #2140[1] into 0.25. The
basic concept - find a data file, load its contents as class
parameters - is pretty darn easy.
It gets more complicated once you want to override the parameters,
though. Say someone has an 'apache' module with an 'apache' class and
an associated data file (in data/apache.yaml in the module). If you
want to change a setting in that file, how would you do so?
I see only two reasonable choices:
1) Have a global 'data' directory in which you could place an
'apache.yaml' file that would get merged with the default one.
2) Support additional search paths for data files, such that earlier
files win out over later files.
#2 has been filed (by me) as #2139[2], and I've got all of the code
done[3], but now I'm wondering whether it's a good idea. Or rather,
Teyo is saying it's not, and I'm having second thoughts.
The upside of #2 is that you can have an arbitrarily deep module path
and have as many overrides as you want, so you've got infinite
flexibility. The downside is that flexibility comes at a significant
manageability cost - it's always going to be hard to know where
exactly a file originated, and it'll be relatively easy to make it so
confusing that it will be unsupportable (I think).
The upside of #1 is that it's wicked-simple. The downside is that
this simplicity might be unnecessarily limiting.
Specifically, if a module ships with a default configuration file and
you want to replace it, then at this point, you have to make a
subclass and override the file source.
Of course, with the global data directory, you could set the file
source as a configuration parameter loaded from the data file, and
then override it in your custom data file.
Another thing that would be more complicated in this global data
directory is managing the needs of multiple groups. You've got your
big module path with hundreds of modules in it and five departments
providing different modules. Each of these departments is going to
override their modules in different ways, but you'll probably have
some common overrides, too.
Maybe we have a data search path, just like everything else?
What do you think?
Either way, it seems unlikely that I'll be merging #2139 in for 0.25.
I might still take a carck at #2140, but it'll be a much smaller,
simpler form. At most, it'll have the global data directory.
However, in thinking about it, I think it could use a bit more
formality before I implement it, so I might wait on 0.26. In the
meantime, there's the data function I published to the list a while
back that people can use to emulate this functionality.
1 - http://projects.reductivelabs.com/issues/2140
2 - http://projects.reductivelabs.com/issues/2139
3 - http://github.com/lak/puppet/tree/tickets/master/2139
--
Reality is that which, when you stop believing in it, doesn't go
away. -- Philip K. Dick, "How to Build a Universe"
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com
>
> Hi all,
>
> We surprisingly haven't had much discussion of the basic function of
> modules since we created them about two years ago, but I'm looking for
> design help on them now.
>
This discussion should also include files and templates since these
have similar requirements as to external data files. The goal for all
of this is to make modules generic. Once it's decided which method to
implement for data, the same should apply to templates and files.
>> I see only two reasonable choices:
>>
>> 1) Have a global 'data' directory in which you could place an
>> 'apache.yaml' file that would get merged with the default one.
> I think this is too limiting. In the tens of seconds since I
> finished this email I can already think of times this would be
> frustrating.
>
Can you explain and give examples?
>> 2) Support additional search paths for data files, such that earlier
>> files win out over later files.
In keeping with the KISS approach I would be concerned about #2.
Stuff like #2 would make it much harder to test and debug results. It
could lead to unexpected output and be a maintenance nightmare.
<soapbox>
IMHO, one of Puppet's overall goals in modeling the sysadmin domain is
to try to make it as hard as possible to lead down unexpected paths.
I initially was very bummed that Puppet did not have complex 'if'
conditionals (pre 0.24.7) but then also realized that it makes it much
harder to shoot myself in the foot and easier to test. We know for a
fact Puppet at one point has to work on a live system to make changes
(in some cases high availability). Any mistake in that deployed code
could really do some damage, not just on one but possible all managed
nodes. Yes I understand new software should be tested before hand and
would be, but again the more complex the logic the harder to ensure
correctness.
</soapbox>
The third option could be like most package management systems. You
have a libraries/applications that are fixed and config files that can
be customized per custom requirements. When the module is upgraded
the new config files are stored in the same folder but renamed (ie
apache.yaml.new) The downside to this approach is allowing multiple
versions of the same module, what would you do? Should in fact the
idea of multiple modules not be a future feature for puppet modules?
Option #3 is the simplest, but would be the most limiting, but might
not be a bad thing.
My $0.02 I would go with option #1 with allowing for a global data
directory per environment. I think overriding, not merging, is the
best approach to handing this data. That way if a new module is
deployed with new data variables, Puppet will error out and make sure
you are aware of these new variables. Merging could lead to an
unexpected result.
Whatever the solution I would want to make sure enough Puppet users in
mid to large deployments voice their input on the matter.
It sounds like it's a tradeoff between flexibility over easier
development/testing.
-L
--
Larry Ludwig
Reductive Labs
How can we mitigate the cost of #2, then?
>
> Is the plan for these additional search paths to be per-
> environment ? :)
Definitely.
--
Fallacies do not cease to be fallacies because they become fashions.
--G. K. Chesterton
>
>
> On Apr 17, 2009, at 6:21 PM, Luke Kanies wrote:
>
>>
>> Hi all,
>>
>> We surprisingly haven't had much discussion of the basic function of
>> modules since we created them about two years ago, but I'm looking
>> for
>> design help on them now.
>>
>
> This discussion should also include files and templates since these
> have similar requirements as to external data files. The goal for all
> of this is to make modules generic. Once it's decided which method to
> implement for data, the same should apply to templates and files.
Sorry, I didn't make it clear in my original email, but the work I did
fundamentally changes how modules search -- *all* files are searched
through all module paths, rather than just the first found module.
>
>>> I see only two reasonable choices:
>>>
>>> 1) Have a global 'data' directory in which you could place an
>>> 'apache.yaml' file that would get merged with the default one.
>
>> I think this is too limiting. In the tens of seconds since I
>> finished this email I can already think of times this would be
>> frustrating.
>>
>
> Can you explain and give examples?
>
>>> 2) Support additional search paths for data files, such that earlier
>>> files win out over later files.
>
>
> In keeping with the KISS approach I would be concerned about #2.
> Stuff like #2 would make it much harder to test and debug results. It
> could lead to unexpected output and be a maintenance nightmare.
>
> <soapbox>
> IMHO, one of Puppet's overall goals in modeling the sysadmin domain is
> to try to make it as hard as possible to lead down unexpected paths.
Heh. I find this a bit funny since it was at your request that I
filed this ticket and implemented this work. :)
Yeah, I'm going to leave this out of 0.25, to give the ideas time to
bake. If people want to experiment, they can take the function I
published, which provides 90% of this functionality and could be
easily extended to provide the rest.
--
A classic is something that everybody wants to have read and nobody
wants to read. -- Mark Twain
Hey I just mentioned of the issue, I didn't mention how it should be
solved ;-)
>
> My issue is troubleshooting. Puppet is somewhat famous for some fairly
> cryptic error messages. I can see #2 being a real bloody nightmare to
> fix issues with. So I am not against the concept but error handling
> would need to be enhanced a lot.
I agree, and it's this issue that's convinced me this should wait
until it can be done with significantly enhanced error messages.
It actually looks pretty likely that we'll soon have a release whose
main purpose is rethinking modules in general; there's a lot of
opportunity there, and we're not exercising much of it.
--
The optimist proclaims that we live in the best of all possible worlds,
and the pessimist fears that this is true.
-- James Branch Cabell 1879-1958
"Where is the reference file or tempate used to set a specific file ?"
"What should be the reference content of this file ?"
"Which manifest files described this file ?"
"What are the differences between the reference file and the current file ?"
Stéphan Gorget
One of my big goals for module refactoring is to add lots of
introspection capabilities. At the least, if we start loading data
files we need a means of declaring class attributes, so the data
loader knows what's valid, and these attributes should be inspectable.
As to inspecting files and such, that's always going to be harder
because it's all handled at compile time, not parse time.
--
You've got to take the bitter with the sour.
-- Samuel Goldwyn
I just raised #2716 [1] which fundamentally requests a similar thing.
I've thought about this and I currently believe that over-riding classes
in the search path would be a very simple yet very powerful way to
extend puppet's capabilities.
With the current implementation, it is a right royal pain to override
module behaviour. A simple change could make this much easier.
Having slept on it, I think what I'd actually like to see is for puppet
to check for the existence of the *class* rather than the module. So, if
I write "include foo", then puppet should look for:
modulepath1/foo/manifests/init.pp
modulepath2/foo/manifests/init.pp
Further, if I write "include foo/bar", then puppet should look for:
modulepath1/foo/manifests/bar.pp
modulepath2/foo/manifests/bar.pp
and, importantly, it shouldn't stop the search & barf when it finds the
module dir "modulepath1/foo" but can't load
"modulepath1/foo/manifests/init.pp". Instead, it should try the other
elements of the search path, ie. explicitly checking for the class
rather than just the module.
I think this adds a lot of power and usability with adding a great deal
of complexity. I don't buy the argument that this would make puppet
unduly complex - it will actually make it much more simple to customise
"generic" modules without having to re-implement them totally separately.
I would even go so far as to argue that the current behaviour is
unintuitive. I certainly didn't expect puppet to look for just the
module name (ie. the first section of the class name) in the module path
rather than the full class name (yes, I realise it's called "modulepath"
not "classpath"!)
Can we please re-visit this issue?
To be clear, what I want is some way to override classes easily. This
behaviour change is most straight-forward way I've seen to achieve this.
Thanks,
R.
When you say 'override classes easily', what exactly do you mean? I
think I'm especially confused by what you consider 'easily' vs. the
other proposed solutions.
--
The only really good place to buy lumber is at a store where the lumber
has already been cut and attached together in the form of furniture,
finished, and put inside boxes. --Dave Barry
Ok, let me try and explain some more...
I've like to have a generic set of modules/classes that can largely be
used as-is at multiple sites.
Generally, I use something like:
class app {
require app::requires, # any classes this class needs
app::install # install the packages
app::config # config files
app::service # eg. init.d script
app::firewall # eg. iptables definitions
}
I would like to try and keep these modules the same at all sites and
override some of the functionality if required on a site-by-site basis.
For example, at one site, I might use (the default) init.d scripts to
manage services while at another I might use runit. This would mean
using a different app::service class whilst leaving all the rest of the
module the same. Or I might want to run the service on a different port
or restrict to a different set of IP address, ie. use a different
firewall class. At the moment, there's no easy way to do either of these
things.
One approach would be to define as much of the behaviour of the module
as possible with variables and to pass them in via a "settings" class, eg.
class app {
require app::requires, # any classes this class needs
app::settings # define vars used throughout the app
app::install # install the packages
app::config # config files
app::service # eg. init.d script
app::firewall # eg. iptables definitions
}
However, there's no easy way to override a class so this approach
wouldn't work, and it also wouldn't really help in replacing the service
class.
Basically, I'm trying to avoid having to effectively branch all my
modules when I deploy at a different site, thus leaving me a maintenance
headache. If I can keep 80-90% of the modules the same at all sites only
change those things that need to be different then I'd be happy.
R.
Ohad,
I'll ping you in #puppet to discuss.
R.
The solution Arri recommends is what I would also recommend -
something akin to his extlookup function, or the datalookup function I
posted a while ago[1].
As mentioned in the thread below, my goal is to have this incorporated
directly into Puppet as soon as we figure out the best way, but the
function is usable now.
--
It is odd, but on the infrequent occasions when I have been called upon
in a formal place to play the bongo drums, the introducer never seems
to find it necessary to mention that I also do theoretical physics.
-- Richard Feynman
----- "Luke Kanies" <lu...@madstop.com> wrote:
> The solution Arri recommends is what I would also recommend -
> something akin to his extlookup function, or the datalookup function I
>
there are two domains of problem here.
- Configuring the behavior of a module
- Extending the behavior of an existing module.
For the first, use extlookup without question.
The second is more complex, here's a use case.
I found a openvpn module on the net - perhaps from the puppet common modules project - and its a good fit, its configurable using extlookup so i can adjust the outcome of the stuff it already knows how to configure.
Now I build 10 machines but I have new needs, I really need openvpn::config to do more, it needs to put new files down - perhaps the existing module doesn't support per client configs (called ccd in openvpn).
now there might be a relationship between openvpn::install, ::config and ::service and my new code really should *plug into* ::config.
I really would want to put my new config files in openvpn::config without changing the relationships or the existing elegant and configurable structure. Perhaps the openvpn::config class already retrieves some variables from extlookup and most certainly when I extend it I want access to this info.
Today what are my options for possible patterns to achieve this? There's none that ticks all the boxes:
- I can make myopenvpn and include ::service, ::config, ::install and just add some extra stuff in there myopenvpn perhaps with a few before => Class["openvpn::service"]. This doesnt give me access to the extlookup data in the class I'd need to go look and do the same lookups, etc. It also does rather brutal things to the nice relationship models since other cases that require => Class["openvpn::config"] wont have requires on my new extra stuff.
there are more, but you can see the kind of reservations I have to them.
How does ruby aproach this problem? Simple.
class String
def to_whatever
# new code
end
end
Now all my strings have this new ability, sweet and this doesnt break anything, no other classes or strings needs to change, everything can just use this new "foo".to_whatever method.
What if I could do the same with puppet, open up the class openvpn::config, put new "stuff" into it?
class openvpn::config {
file{"newfile":
content => template("newfile.erb")
}
}
This template would have access to variables set in the super class via extlookup I'd simply be extending it.
How to trigger it on a node?
node foo {
load openvpn::extendedconfig
include openvpn::config
.
.
}
now, openvpn::config would have both the content from common modules and my newfile.
Obviously perhaps my suggested syntax is cludgy, I'm more concerned about the concept right now than the how.
I think this captures more what the initial poster had in mind.
--
R.I.Pienaar
One of the many undocumented "features" in Puppet:
class foo {
notify { "foo": }
}
class foo {
notify { "bar": }
}
include foo
Works fine. The only caveat is that if you evaluate 'foo' between the
reopening, the added code won't get evaluated.
Does this provide the behaviour you're asking for? Obviously there
are still limitations - your code has to be loaded manually, can't be
in a module with the same name, etc.
--
Criminal: A person with predatory instincts who has not sufficient
capital to form a corporation. --Howard Scott
OK, initial excitement over, this is awesome but relies on import and import is not overly clever.
We need something like import that understands modulepath and environments, so that import("openvpn/extendedconfig.pp") does the right thing - finds the right openvpn module as per modulepath for this environment and then does a normal import.
Since using modules I never import things it might be that I missed/forgot some crucial bit of trickery with import but I doubt it can do this - dito for the file() function really
--
R.I.Pienaar
Good point; import could be relatively easily extended to fix this.
With that in place, how far would you be from where you think we need
to be?
--
Do you realize if it weren't for Edison we'd be watching TV by
candlelight? -- Al Boliska
I'm feeling warm fuzzies but I need to play a bit, import is a bit of a dirty little thing, something like:
node foo {
openvpn::newstuff extends openvpn::config
include openvpn::config
}
end result would be that - there would be no openvpn::newstuff in the classes list only openvpn::config. Effectively this is exactly an import as discussed, just a different syntax, you'll go find the file using the module search method and just import it.
this would be more obvious about whats happening, what this file does etc, the intend is clearer than with import.
Ofcourse this means external nodes and so forth need the same ability,
I think perhaps this extends style syntax might be 1.0 target while a fixed up import might be 0.25.6? Give us some time to give this a spin and work out what we're missing.
--
R.I.Pienaar
I was thinking exactly this kind of syntax, and it'd be pretty easy to
implement.
> this would be more obvious about whats happening, what this file
> does etc, the intend is clearer than with import.
>
> Ofcourse this means external nodes and so forth need the same ability,
>
> I think perhaps this extends style syntax might be 1.0 target while
> a fixed up import might be 0.25.6? Give us some time to give this a
> spin and work out what we're missing.
Do you need the fixed-up import, at that point? And what specifically
do you want done to it?
--
Happiness is not achieved by the conscious pursuit of happiness; it is
generally the by-product of other activities. -- Aldous Huxley
The new proposed syntax would be good enough but I think the improved import would be helpful anyway see below.
The new syntax made me realize another requirement - not satisfied by import - the extensions has to be node/class specific.
A common pattern is site modules or client modules that get used to apply specifics to a site based on your usual modules, sticking with openvpn:
node ovpn1.CLIENT1.com {
client1::openvpn::config extends openvpn::config
include openvpn
}
ditto for client 2, in client1::openvpn::config I would enable a client to add ccd vpn records perhaps using a utility define offered by the main openvpn:: module, you'd still want them to happen as part of openvpn::config so this wold be great, but, client2 shouldn't get client1's extensions.
So, new syntax to enable this, fixed up import just to make it more awesome than present, and perhaps as a means of global extends otherwise we could just make this new syntax work in site.pp if someone did want to extend openvpn::config globally
--
R.I.Pienaar
This doesn't seem any different than what I thought we were already
talking about:
class openvpn { ... }
class client1::openvpn extends openvpn { ... }
class client2::openvpn extends openvpn { ... }
node default {
include "$hostname::openvpn"
}
Or am I missing something?
> So, new syntax to enable this, fixed up import just to make it more
> awesome than present, and perhaps as a means of global extends
> otherwise we could just make this new syntax work in site.pp if
> someone did want to extend openvpn::config globally
Can you open feature requests documenting the behaviour you're looking
for?
--
You can't wait for inspiration. You have to go after it with a club.
-- Jack London
Well if we're just using import for this once something has been imported it would be visible to the whole manifest, so if client1 extends ::config then client2 would get those extensions too, afaik, again I'm not a big user of import but this seems right with what i remember so worth clarifying.
>
> > So, new syntax to enable this, fixed up import just to make it more
>
> > awesome than present, and perhaps as a means of global extends
> > otherwise we could just make this new syntax work in site.pp if
> > someone did want to extend openvpn::config globally
>
> Can you open feature requests documenting the behaviour you're looking
> for?
will do over the weekend
--
R.I.Pienaar
I don't really see the difference as well.
> Well if we're just using import for this once something has been
> imported it would be visible to the whole manifest, so if client1
> extends ::config then client2 would get those extensions too, afaik,
> again I'm not a big user of import but this seems right with what i
> remember so worth clarifying.
if you use only autoloading and inheritance you don't need to use
import at all to achieve your goal. At least I think this is what I'm
doing to solve this kind of problem and it works pretty well. I don't
yet see why you would need any kind of mixins. If you still think you
need it, could somebody explain why you would need that instead of
inheritance and inclusion?
cheers pete
> >> [...]
> >> This doesn't seem any different than what I thought we were
> already
> >> talking about:
> >>
> >> class openvpn { ... }
> >>
> >> class client1::openvpn extends openvpn { ... }
> >> class client2::openvpn extends openvpn { ... }
> >>
> >> node default {
> >> include "$hostname::openvpn"
> >> }
> >>
> >> Or am I missing something?
>
> I don't really see the difference as well.
Earlier in the thread we proposed using import to solve this problem, my point is that it wont work cos it will expose client1's extended config to client2.
>
> > Well if we're just using import for this once something has been
> > imported it would be visible to the whole manifest, so if client1
> > extends ::config then client2 would get those extensions too, afaik,
> > again I'm not a big user of import but this seems right with what i
> > remember so worth clarifying.
>
> if you use only autoloading and inheritance you don't need to use
> import at all to achieve your goal. At least I think this is what I'm
> doing to solve this kind of problem and it works pretty well. I don't
> yet see why you would need any kind of mixins. If you still think you
> need it, could somebody explain why you would need that instead of
> inheritance and inclusion?
You should read my earlier mail that explains the use case.
http://groups.google.com/group/puppet-dev/msg/db8e93c12207f79d
--
R.I.Pienaar
ah right. Currently I would do this kind of things one one side with
inheritance (if I want to access variables from openvpn::config) and
with inclusion if I want to set certain variables first:
class openvpn { include openvpn::config }
class openvpn::config {
$somebar = 'helo'
file{'some_file': content => "helo" }
}
class myopenvpn inherits openvpn{
$foobar = root
include myopenvpn::config
}
class myopenvpn::config inherits openvpn::config {
File['some_file']{ owner => $foobar }
notice("$somebar")
}
I'm not (yet ;) ) really arguing against your proposed solution, I'm
just trying to understand the problem you'd like to have solved.
cheers pete
----- "Peter Meier" <peter...@immerda.ch> wrote:
> >> > Well if we're just using import for this once something has been
> >> > imported it would be visible to the whole manifest, so if
> client1
> >> > extends ::config then client2 would get those extensions too,
> afaik,
> >> > again I'm not a big user of import but this seems right with what
> i
> >> > remember so worth clarifying.
> >>
> >> if you use only autoloading and inheritance you don't need to use
> >> import at all to achieve your goal. At least I think this is what
> I'm
> >> doing to solve this kind of problem and it works pretty well. I
> don't
> >> yet see why you would need any kind of mixins. If you still think
> you
> >> need it, could somebody explain why you would need that instead of
> >> inheritance and inclusion?
> >
> > You should read my earlier mail that explains the use case.
> >
> > http://groups.google.com/group/puppet-dev/msg/db8e93c12207f79d
>
> ah right. Currently I would do this kind of things one one side with
> inheritance (if I want to access variables from openvpn::config) and
> with inclusion if I want to set certain variables first:
My example, the original poster, the ticket and Nigel (I think) pointed out that the approach below wreaks havok with dependencies and requires and you end up basically having a weird dangling class that does not form part of the original intended ordering and dependencies of the module.
For example, you would want to let openvpn::service depend on myopenvpn::config below, how would you do this without editing openvpn::service? And what if openvn::service come from a module repository that gets updated at times?
> class openvpn { include openvpn::config }
>
> class openvpn::config {
> $somebar = 'helo'
> file{'some_file': content => "helo" }
> }
>
>
> class myopenvpn inherits openvpn{
> $foobar = root
> include myopenvpn::config
> }
>
> class myopenvpn::config inherits openvpn::config {
> File['some_file']{ owner => $foobar }
> notice("$somebar")
> }
This would essentially be a run-many-times-till-it-works approach rather than a clean dependency based solution, unless you put in a lot of before => properties in these classes, and this too would not really do it properly.
>
> I'm not (yet ;) ) really arguing against your proposed solution, I'm
>
> just trying to understand the problem you'd like to have solved.
resources in openvpn::service requires Class["openvpn::config"] and this cleanly ensures all config happens before the service happens, I think is essential in achieving the goal of sharing modules that are not just configurable but also extendable to local needs
--
R.I.Pienaar
agree. But I have to admit, that I would simply order the things
different to avoid that.
>> I'm not (yet ;) ) really arguing against your proposed solution, I'm
>>
>> just trying to understand the problem you'd like to have solved.
>
>
> resources in openvpn::service requires Class["openvpn::config"] and
> this cleanly ensures all config happens before the service happens,
> I think is essential in achieving the goal of sharing modules that
> are not just configurable but also extendable to local needs
right, I see it now. Actually I usually don't specify dependencies on
class level, as I would order it anyway different. But I see that for
the way you are doing the class setups you would need that.
Thanks for your explanations.
cheers pete
> > This would essentially be a run-many-times-till-it-works approach
> > rather than a clean dependency based solution, unless you put in a
> > lot of before => properties in these classes, and this too would not
> really do it properly.
>
> agree. But I have to admit, that I would simply order the things
> different to avoid that.
yeah, the main issue here is modules from something like common modules, or people like me who has a core set of modules in use by many clients. but as clients need change I need a way to put in site specific differences.
>
> >> I'm not (yet ;) ) really arguing against your proposed solution, I'm
> >> just trying to understand the problem you'd like to have solved.
> >
> >
> > resources in openvpn::service requires Class["openvpn::config"] and
> > this cleanly ensures all config happens before the service happens,
> > I think is essential in achieving the goal of sharing modules that
> > are not just configurable but also extendable to local needs
>
> right, I see it now. Actually I usually don't specify dependencies on
> class level, as I would order it anyway different. But I see that for
> the way you are doing the class setups you would need that.
If you're writing modules for many distributions, or complex ones with lots resources per class, you can't just say:
notify => Service["apache2"]
or
require => File["/etc/apache2/conf.d/foo"]
Because redhat has Service["httpd"] and so forth and so on, if the OS specific code is in apache::service I can just do notify => Class["apache::service"] and it will always do the right thing, Same I wouldn't want to maintain a huge require list for files - and I wouldnt know what the operating systems do, requiring the class solves that problem.
Same for requires etc, I wrote more about the structure and needs for it here http://www.devco.net/archives/2009/09/28/simple_puppet_module_structure.php
--
R.I.Pienaar
I solve that with aliases, at least the one with services:
http://git.puppet.immerda.ch/?p=module-apache.git;a=blob;f=manifests/centos.pp;h=64af2d293c795ba8aee9fff13ea6c24b0b2ee8c7;hb=977ae4ccebde5554fb1793eb093af98778852fb7#l5
and I usually then always do notify => Service["apache2"]
require => File["/etc/apache2/conf.d/foo"] I would wrap anyway into a
define to distinguish locations for different distros and then simply
require the define.
I didn't yet come find a limitation with this solution, but yeah there
might be one, some day.
cheers pete
>> This doesn't seem any different than what I thought we were already
>> talking about:
>>
>> class openvpn { ... }
>>
>> class client1::openvpn extends openvpn { ... }
>> class client2::openvpn extends openvpn { ... }
>>
>> node default {
>> include "$hostname::openvpn"
>> }
>>
>> Or am I missing something?
>
>
> Well if we're just using import for this once something has been
> imported it would be visible to the whole manifest, so if client1
> extends ::config then client2 would get those extensions too, afaik,
> again I'm not a big user of import but this seems right with what i
> remember so worth clarifying.
Ah, I see - yeah, you'd need the extension to be a compile-time
operation, not a parse-time operation. I see.
--
Now and then an innocent man is sent to the legislature.
--Kin Hubbard