Cross-module dependencies

428 views
Skip to first unread message

jcbollinger

unread,
Jan 26, 2012, 12:00:28 PM1/26/12
to Puppet Users
Since Felix seems not to have gotten around to doing this in the past
couple of days, or else was unable to do so,

On Jan 24, 3:28 am, Felix Frank <felix.fr...@alumni.tu-berlin.de>
wrote:
>
> there was a discussion in the "can we deprecate defined() in Telly"
> thread about how we can even begin to design Forge modules without it.
>
> A recurring problem is that multiple modules rely on certain packages,
> and there is no good model (yet) to unite their resource declarations.
> Therefore it's a common (although imho disgusting) workaround to do
> things like
> if !defined(Package[foo]) { package { "foo": ensure => installed } }
>
> On 01/20/2012 11:34 PM, Cody wrote:
>
> > Defining all somewhat common packages in a central location becomes
> > unrealistic when you no longer "control" the code that is in every
> > module you use. If you obtain five modules from the forge and they
> > all require a specific package and so all define that package your not
> > going to convince, nor is it a good design to require everyone to move
> > the package definitions from that collection of modules. They need to
> > function as a collection out of the box.
>
> Agreed. How can this be accomplished?


I'm not sure it can be, though I have some ideas for how we can do
better than we do now.

To start, consider the extreme case where modules have conflicting
essential requirements. For example, they require packages that
declare conflicts with each other, or they differ about whether some
service must be enabled or disabled. Such modules cannot *ever* work
correctly together, out of the box or otherwise, therefore it is
impossible to achieve a system that ensures that any random collection
of modules will automatically work together.

Furthermore, I think that its probably the wrong goal that even
compatible modules should always work together automatically. The
goal should be that compatible modules work together without
modification, but it is altogether reasonable for that to require
extra code somewhere else (e.g. extra classes assigned to the node,
intermodule relationships defined, etc.).


> Perhaps there needs to be some kind of "Forge common" module that by
> policy can only ever declare virtual resources (packages are a prominent
> example).
> A user who wishes to retain the capability of using modules from the
> Forge would be required to install this common module, and replace their
> own resource declarations with realizations of the common resources.
> For this to work, it's definitely a plus that you can override
> attributes in collections:
> Package<| title == "apache2": |> { ensure => "2.2.12" }
> ...although that does bear some caveats. Does this still work in recent
> versions?
>
> If we can take this for granted, all Forge modules can adhere to that
> same standard.
>
> This is a rough sketch of how things might possibly work, and surely has
> lots of wrinkles of its own. Still, I'm quite sure we need a proper way
> to rid ourselves of the horror that is the parse order dependent check
> for defined resources ;-)


If we must rely only on features already present in Puppet, then I
think that's heading in the right direction. I doubt it's feasible to
rely on a single "Forge Common" module, however. Aside from the
problem of maintaining a "Common" module as other modules are created
and maintained, there is also the same essential problem I began with:
different modules may have conflicting requirements.

With respect to a given module, we need to distinguish between two
three types of resources:
1) Resources owned by that module
2) All other resources

Modules provide definitions of resources that they own. For the most
part, those definitions should be virtual to avoid unnecessary inter-
module coupling, but some resources are reasonable to define
concretely. Modules may realize virtual resources belonging to other
modules (without necessarily needing to know which module actually
provides the definition), but they must not override properties of
resources they do not own.

The set of resources owned by a module and the set of other resources
it depends on are parts of its external interface, and modules whose
sets of owned resources overlap are inherently incompatible. Because
of that source of incompatibility, modules should seek to keep their
lists of owned resources small. But how, then, can optional inter-
module dependencies be handled, or equivalently, how can a module be
made able both to interoperate and to stand on its own? I see two
elements to this:

1) It is the site's responsibility to ensure that all "other"
resources required by each module in use be provided. That is the
role that Felix's "Forge Common" module is aimed at, though I don't
much care for that particular solution. Instead, I think in many
cases it will be necessary for sites to provide at least some resource
definitions via local custom modules.

2) Modules can ease the task for users by providing *optional* classes
virtually defining some or all of the "other" resources they need.
Where there are no conflicts to deal with, users could assign those
classes to nodes along with the module's main classes, and thereby
avoid any need to define the needed resources themselves.


John

jcbollinger

unread,
Jan 26, 2012, 12:48:38 PM1/26/12
to Puppet Users

On Jan 26, 11:00 am, jcbollinger <John.Bollin...@stJude.org> wrote:
> If we must rely only on features already present in Puppet, then I
> think that's heading in the right direction.

On the other hand, if we can wish for new features to address this
area, then there may be altogether different approaches available. In
particular, it is useful to recognize that dependencies are not just
on a particular resource generally -- rather, they are on a particular
resource having certain specific properties. For example, a web
server module doesn't just rely on, say, Package['httpd']. Rather, it
relies on that package having an 'ensure' parameter different from
'absent' and 'purged'. Puppet DSL does not currently have a means to
express that.

Consider, then, a new metaresource type, Constraint. The purpose of
the Constraint resource type would be to allow multiple unrelated
classes to collaborate on defining the properties of a single
resource, and it would do so by allowing classes to limit the values
that chosen resource properties may have.

At compilation time, Puppet would collect and combine all the
constraints on any particular resource, and use the results to set
unspecified property values and validate specified ones. Usage might
look something like this:

constraint { 'webserver-httpd_package-present':
resource => Package['httpd'],
property => 'ensure',
forbidden_value => [ 'absent', 'purged' ],
# also available: allowed_value
# maybe: default_value
}

Not only would this nicely meet the needs of different modules to
express their requirements on shared resources, it would also make it
much easier to recognize resource conflicts. If Puppet automatically
generated empty resource definitions to constrain when it discovered
constraints on otherwise-undefined resources, then that would also
overcome the problem of deciding where to define particular resources.

I can imagine many -- perhaps most -- resource definitions being
replaced or supplemented by constraint declarations.


John

Felix Frank

unread,
Jan 27, 2012, 8:20:12 AM1/27/12
to puppet...@googlegroups.com
Hi John,

thanks for coming up with such elaborate ideas, your input to this group
adds a lot of meat to many discussions.

I can agree with a lot of what you wrote, barring the following remarks:

On 01/26/2012 06:00 PM, jcbollinger wrote:
> Modules provide definitions of resources that they own. For the most
> part, those definitions should be virtual to avoid unnecessary inter-
> module coupling, but some resources are reasonable to define
> concretely.

Jeff has made a strong point against using virtual resources in modules
at all, causing me to shift my own views as well.
If I understand him correctly, one of the chief problems is the high
probability of accidental collection/realisation of such resources by
the end user's manifest.

On 01/26/2012 06:48 PM, jcbollinger wrote:
> I can imagine many -- perhaps most -- resource definitions being
> replaced or supplemented by constraint declarations.

The model is intriguing, but gives me another usability headache.
Wouldn't this put an end to self-contained modules?

I wrote in a latter mail (is this the same thread? Sorry, I use this
only through Thunderbird and get confused sometimes) how I see need for
explicit module dependencies and a system that can automatically
download required modules from the forge. I can see this supplementing
your idea of constraints nicely, but without it, downloading modules
could quickly become a nightmare for users.

Cheers,
Felix

Walter Heck

unread,
Jan 27, 2012, 8:52:45 AM1/27/12
to puppet...@googlegroups.com
Hello,

On Fri, Jan 27, 2012 at 15:20, Felix Frank
<felix...@alumni.tu-berlin.de> wrote:
> how I see need for
> explicit module dependencies and a system that can automatically
> download required modules from the forge. I can see this supplementing
> your idea of constraints nicely, but without it, downloading modules
> could quickly become a nightmare for users.

There's something else we need to think about here. Some modules have
a soft/conditional requirement for other modules. What I mean is that
if you don't use certain parts of a module, you don't need the module
that that part of the code refers to. the only decent way I can come
up with to solve that is to use what for instance in C is done with
#IFDEF. That way the module could just ignore modules that it doesn't
_really_ require.

I for instance have modules that allow you to use different backends
for monitoring or backups. If requirements were done automatically
based on the whole module, it would need a myriad of other modules,
only one of which is ever used.

cheers,
--
Walter Heck

--
follow @walterheck on twitter to see what I'm up to!
--
Check out my new startup: Server Monitoring as a Service @ http://tribily.com
Follow @tribily on Twitter and/or 'Like' our Facebook page at
http://www.facebook.com/tribily

Nick

unread,
Jan 27, 2012, 8:58:11 AM1/27/12
to puppet...@googlegroups.com
On 26/01/12 17:48, jcbollinger wrote:
> In particular, it is useful to recognize that dependencies are not just on a
> particular resource generally -- rather, they are on a particular resource
> having certain specific properties.

Yes.

Also: currently in Puppet one cannot say anything about a resource without a
declaration that you will "manage" it. (Unless perhaps that state happens to be
encapsulated by a Fact, which typically isn't and in many cases couldn't
feasibly be - like the case of file attributes.)

Therefore many "dependencies" are created only because of a need to check some
state of a resource - which one may not want or need to manage.


> Consider, then, a new metaresource type, Constraint. The purpose of the
> Constraint resource type would be to allow multiple unrelated classes to
> collaborate on defining the properties of a single resource, and it would do
> so by allowing classes to limit the values that chosen resource properties
> may have.
>
> At compilation time, Puppet would collect and combine all the constraints on
> any particular resource, and use the results to set unspecified property
> values and validate specified ones. Usage might look something like this:
>
> constraint { 'webserver-httpd_package-present':
> resource => Package['httpd'],
> property => 'ensure',
> forbidden_value => [ 'absent', 'purged' ],
> # also available: allowed_value
> # maybe: default_value
> }
>
> Not only would this nicely meet the needs of different modules to express
> their requirements on shared resources, it would also make it much easier to
> recognize resource conflicts. If Puppet automatically generated empty
> resource definitions to constrain when it discovered constraints on
> otherwise-undefined resources, then that would also overcome the problem of
> deciding where to define particular resources.
>
> I can imagine many -- perhaps most -- resource definitions being replaced or
> supplemented by constraint declarations.


Here's a slightly different angle.

(Note, I'll use capitalisation distinguish between the "resources" that exist
outside Puppet, and the "Resource" instances inside, which model them.)

I think there *is* a case to be made that Puppet needs a new "kind" of Resource
declaration. One which promises never to change the state of anything.

Immutability is, I think, the key to allowing multiple declarations of this sort
co-exist. Resources currently have to uniquely "own" a resource so that they can
safely change it. As I said, one doesn't always need or want that ownership: we
know the kind of baggage it carries.

Ideally we'd be able to separate out the aspects of a Resource which merely
assert what *should* be the case (ensure => present etc.) from those bits which
would then change the state of the resource if it deviates.

For the sake of discussion I'll call that former kind of declaration an "Assertion".

To briefly address the points from earlier email: Nan asked how would one
address merging, with respect to the following aspects of a Resource?

a) unifying before/requires attributes
b) unifying if/then/else constructs
c) auditing changes back to their source
d) unifying hash/array attributes

Well, when using Resources, yes, these make them very hard to unify in general.

However - although this needs more thought - if we could invent some way to
declare mere Assertions, they might do instead of Resources for many cases, and
it might be possible to unify them more simply, because:

a) Problems related to ordering mostly disappear,

...since nothing is being changed. (Of course, external things which might
change things could be a problem.)

b) If/then/else clauses can still make sense within definitions of custom
Assertions,

...if they can compose only other Assertions, and the conditions don't change.

c) Auditing is not a problem: since nothing is changed, there is nothing to
audit.

d) Hash/array attribute values would need to be resolved on a case-by-case
basis,

...depending on the semantics. When hash and arrays are semantically
representing are sets, this should be a straightforward "and" or "or" operation.
When the order matters, like a search path, or there is no obvious way to unify
two attributes, then this is an unresolvable contradiction and should generate
an error. There may be cases in between.

Possibly this doesn't fit all the use-cases which run into cross-module
dependency problems, but might significantly reduce the need to create the
dependencies in the first place.

Anyway, I need to get back to work, I'll try to say more in a later email.


Cheers,

N

Felix Frank

unread,
Jan 27, 2012, 9:01:21 AM1/27/12
to puppet...@googlegroups.com
Hi,

On 01/27/2012 02:52 PM, Walter Heck wrote:
> There's something else we need to think about here. Some modules have
> a soft/conditional requirement for other modules. What I mean is that
> if you don't use certain parts of a module, you don't need the module
> that that part of the code refers to. the only decent way I can come
> up with to solve that is to use what for instance in C is done with
> #IFDEF. That way the module could just ignore modules that it doesn't
> _really_ require.

thanks for pointing this out, but it has been covered (I think) in
another thread already:

On 01/19/2012 09:17 PM, Nick Fagerlund wrote:
> So, you can conditionally declare the rule if the defined type is
> available to the autoloader, and otherwise you don't attempt to manage
> the firewall and expect that the user has read the documentation and
> will make a hole for the service themselves.
>
> if defined(firewall::iptables::rule) {
> firewall::iptables::rule {'mysql_server':
> ...etc. etc.
> }
> }
>
> See? It's just a way to peek around at what the user has installed.

Thanks again to Nick for this quote, it keeps proving useful ;-)

Cheers,
Felix

jcbollinger

unread,
Jan 27, 2012, 10:22:06 AM1/27/12
to Puppet Users


On Jan 27, 7:20 am, Felix Frank <felix.fr...@alumni.tu-berlin.de>
wrote:
> Hi John,
>
> thanks for coming up with such elaborate ideas, your input to this group
> adds a lot of meat to many discussions.
>
> I can agree with a lot of what you wrote, barring the following remarks:
>
> On 01/26/2012 06:00 PM, jcbollinger wrote:
>
> > Modules provide definitions of resources that they own.  For the most
> > part, those definitions should be virtual to avoid unnecessary inter-
> > module coupling, but some resources are reasonable to define
> > concretely.
>
> Jeff has made a strong point against using virtual resources in modules
> at all, causing me to shift my own views as well.
> If I understand him correctly, one of the chief problems is the high
> probability of accidental collection/realisation of such resources by
> the end user's manifest.


The core problem to solve is that multiple modules may depend on the
same resources, but each resource can be defined only once.

If modules' resource requirements were invariant then I could be
satisfied with documenting them and requiring the user to provide them
concretely; indeed, that's not very far from what I just proposed. On
the contrary, however, whether a particular resource is needed by some
module may depend on which of that module's classes are used, and on
which parameters and external data are supplied. This approach
therefore has its own inherent problem for users: to avoid declaring
unneeded resources.

Given a choice between the problems of avoiding declaring unneeded
resources and of avoiding collecting unwanted resources, I would
choose the latter. It is easier to deal with, and failure to
completely solve it is no more costly than failure to solve the
alternative problem.


> On 01/26/2012 06:48 PM, jcbollinger wrote:
>
> > I can imagine many -- perhaps most -- resource definitions being
> > replaced or supplemented by constraint declarations.
>
> The model is intriguing, but gives me another usability headache.


From a usability perspective, I think this is a far better proposal
than anything else on the table:
(+) It enables modules to declare their requirements more precisely,
reducing the risk of modules compiling and applying correctly, but
nevertheless failing to achieve the desired configuration.
Constraints are the only proposal currently on the table that provide
that.
(+) A key aspect is that it allows Puppet to pinpoint and diagnose
conflicts at compilation time.
(+) Furthermore, it allows each module to express *only* its
requirements, ignoring properties that it doesn't care about (though
other modules might care about them). In fact, it rewards modules for
doing so by making them more interoperable.
(+) It does not rely on virtual resources (though it is compatible
with them), so it does not impose any risk of accidental resource
collection.
(+) It could be implemented in a manner that forestalls inter-module
parse order dependencies.

The main cost I see is:
(-) It creates a new distinction between resource properties that are
explicitly specified in resource definitions and those that are not.
For this reason (at least), such a feature could not be introduced
until Telly at the earliest.

That cost is incurred, however, only in manifest sets that use
Constraints, and then only with respect to resources that are targeted
by Constraints. It is anyway not a usability issue, but rather an
upgrade / conversion / compatibility issue.


> Wouldn't this put an end to self-contained modules?


No. What makes you think it would? I think it allows modules to be
*more* self-contained than they currently are.


> I wrote in a latter mail (is this the same thread? Sorry, I use this
> only through Thunderbird and get confused sometimes) how I see need for
> explicit module dependencies and a system that can automatically
> download required modules from the forge. I can see this supplementing
> your idea of constraints nicely, but without it, downloading modules
> could quickly become a nightmare for users.


I have considered that possibility, essentially a package management
system for Puppet modules. Although such a thing could undoubtedly be
implemented, I don't think it would be very suitable for addressing
the inter-module dependency problem. Package management systems such
as yum and apt rely on the fact that software dependency graphs are
more or less trees, but I don't think module dependency graphs tend to
be anywhere near as treelike. I suppose module repository managers
could enforce tree-shaped dependency graphs, but that imposes the
tool's requirements on the problem space instead of relying on a tool
that suits well in the first place.

I do not mean to say that a tool that automatically downloaded and
installed modules from the Forge would be useless -- far from it. I
just don't think that it would adequately address the inter-module
dependency issue, and therefore I would recommend that such a tool not
even try to do so. It solves an altogether different problem.


John

Felix Frank

unread,
Jan 27, 2012, 11:07:23 AM1/27/12
to puppet...@googlegroups.com
Hi,

On 01/27/2012 04:22 PM, jcbollinger wrote:
> From a usability perspective, I think this is a far better proposal
> than anything else on the table:

I've thought of another plus. Even though the design proposal adds to
the DSL (and complexity is generally to be avoided), it does so in a
manner that will not make it necessary for novices (or any end user) to
deal with the particular featureset, but instead limits its target
audience to developers of public modules.

>> Wouldn't this put an end to self-contained modules?
>
> No. What makes you think it would? I think it allows modules to be
> *more* self-contained than they currently are.

I was thinking along the lines of "currently modules will just install
the packages they need", for instance. But you're right of course -
currently modules can break each other because of this, so a way for the
compiler to clearly pinpoint reasons and locations of mismatches would
indeed be superior.

> I do not mean to say that a tool that automatically downloaded and
> installed modules from the Forge would be useless -- far from it. I
> just don't think that it would adequately address the inter-module
> dependency issue, and therefore I would recommend that such a tool not
> even try to do so. It solves an altogether different problem.

Again, I'm inclined to concur. The issue at hand is to stop modules from
breaking each other horribly. Once that's off the table, a module
management system is a whole new issue in and of itself.

Cheers,
Felix

jcbollinger

unread,
Jan 27, 2012, 11:59:08 AM1/27/12
to Puppet Users


On Jan 27, 8:01 am, Felix Frank <felix.fr...@alumni.tu-berlin.de>
wrote:
> Hi,
>
> On 01/27/2012 02:52 PM, Walter Heck wrote:
>
> > There's something else we need to think about here. Some modules have
> > a soft/conditional requirement for other modules. What I mean is that
> > if you don't use certain parts of a module, you don't need the module
> > that that part of the code refers to. the only decent way I can come
> > up with to solve that is to use what for instance in C is done with
> > #IFDEF. That way the module could just ignore modules that it doesn't
> > _really_ require.
>
> thanks for pointing this out, but it has been covered (I think) in
> another thread already:
>
> On 01/19/2012 09:17 PM, Nick Fagerlund wrote:

[...]

And Nick's approach seems good and useful, but I don't think it
addresses Walter's concern. Walter observes that modules' runtime
inter-module dependencies can depend on how they are used, and he
argues, I think, that that complicates the problem of managing such
dependencies via a package management system.

For example, if module X depends on module Y only for feature
X::ySupport, and I intend never to use X::ySupport, then I might like
to avoid installing module Y. If I furthermore want to use module Y2
that is incompatible with module Y, then I might *need* to avoid
installing Y. There are ways to handle problems such as those, but it
all creates a lot of overhead to make everything work together
properly in such a scheme.


John
A hollow voice says "Plugh".

jcbollinger

unread,
Jan 27, 2012, 1:27:24 PM1/27/12
to Puppet Users


On Jan 27, 7:58 am, Nick <oinksoc...@letterboxes.org> wrote:
> On 26/01/12 17:48, jcbollinger wrote:
>
> > In particular, it is useful to recognize that dependencies are not just on a
> > particular resource generally -- rather, they are on a particular resource
> > having certain specific properties.
>
> Yes.
>
> Also: currently in Puppet one cannot say anything about a resource without a
> declaration that you will "manage" it. (Unless perhaps that state happens to be
> encapsulated by a Fact, which typically isn't and in many cases couldn't
> feasibly be - like the case of file attributes.)


Of course, this is a question of scale and foreknowledge. One can
easily create a custom fact expressing any or all of the attributes of
a specific file. I think one could even do it for all of the files in
a directory, or even for an entire directory tree. Before too long,
however, that's going to become quite slow, and it would be difficult
to use on the Puppet side. I can't think of a persistent node state
detail that couldn't be expressed via a custom fact, but if you need
many such details, or if you can't be sure in advance which you will
need, then you do have a problem.


> Therefore many "dependencies" are created only because of a need to check some
> state of a resource - which one may not want or need to manage.


It sounds like you want to perform conditional logic on the agent. I
am not at all comfortable with that idea, though perhaps I could
become more so. It entails a much more fundamental change to Puppet
than anything else we have been discussing. Or did you have something
different in mind?


> Here's a slightly different angle.

[...]

> Ideally we'd be able to separate out the aspects of a Resource which merely
> assert what *should* be the case (ensure => present etc.) from those bits which
> would then change the state of the resource if it deviates.
>
> For the sake of discussion I'll call that former kind of declaration an "Assertion".

[...]

> Possibly this doesn't fit all the use-cases which run into cross-module
> dependency problems, but might significantly reduce the need to create the
> dependencies in the first place.


I could see implementing a version of your Assertion idea as a check-
only Constraint, but I don't think there is yet an infrastructure to
support that part. However, I don't see at all how the Assertion idea
fits into the cross-module dependency picture. Perhaps this is
because I don't see the purpose of declaring resources that you do not
intend to manage.

When I consider cross-module dependencies, I think the usual case is
that a module *does* want ensure that particular resource properties
are managed, but it may not care about other properties, and it cares
about who owns Resources only insofar as someone has to own them and
it affects who can manage them. I can imagine wanting to _assert_
Resource properties only as a workaround for not being able actually
to manage them. Are there other reasons to want such a thing?


> Anyway, I need to get back to work, I'll try to say more in a later email.


That would help me to determine what I think about the idea. As it
is, I suspect I don't quite understand what you are hoping to
accomplish with it.


John

Trevor Vaughan

unread,
Jan 28, 2012, 10:35:38 AM1/28/12
to puppet...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've been mulling this over and wanted to get opinions on an, ugly, but completely functional approach.

We've been talking about people supplying metadata to describe inter-class dependencies (inter-module dependencies really, but hear me out). With the
advent of the parameterized class, you could simply write all ifdef dependencies directly into a parameter like follows:

# Get this for the 'contains' function
include 'stdlib'

class foo (
# No requirements by default
$reqs = ['']
) {
if contains($reqs,'bar') {
include 'bar'
}

...some stuff...

if contains($reqs, 'bar') {
bar::baz { 'something': ... }
}
}

It's not elegant by any means, but it is functional and since (in theory) puppet only includes a class once, then all of the various includes would be
completely skipped.

If would be nice if, in this example, $reqs was actually a class metaparameter and Puppet would automatically try to include the class when passed
into that variable.

Benefits:

* Works with the current language structure
* Does what you want it to in terms of not needing defined

Drawbacks:

* Requires the user to have an explicit working knowledge of all modules and namespaces
* Adds a lot of random logic to the code (unless it becomes a metaparam of some sort)

Thanks,

Trevor

On 01/27/2012 08:52 AM, Walter Heck wrote:
> Hello,
>
> On Fri, Jan 27, 2012 at 15:20, Felix Frank
> <felix...@alumni.tu-berlin.de> wrote:
>> how I see need for
>> explicit module dependencies and a system that can automatically
>> download required modules from the forge. I can see this supplementing
>> your idea of constraints nicely, but without it, downloading modules
>> could quickly become a nightmare for users.
> There's something else we need to think about here. Some modules have
> a soft/conditional requirement for other modules. What I mean is that
> if you don't use certain parts of a module, you don't need the module
> that that part of the code refers to. the only decent way I can come
> up with to solve that is to use what for instance in C is done with
> #IFDEF. That way the module could just ignore modules that it doesn't
> _really_ require.
>
> I for instance have modules that allow you to use different backends
> for monitoring or backups. If requirements were done automatically
> based on the whole module, it would need a myriad of other modules,
> only one of which is ever used.
>
> cheers,

- --
Trevor Vaughan
Vice President, Onyx Point, Inc.
email: tvau...@onyxpoint.com
phone: 410-541-ONYX (6699)
pgp: 0x6C701E94

- -- This account not approved for unencrypted sensitive information --
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iQEcBAEBAgAGBQJPJBXGAAoJECNCGV1OLcypcXkH/3Y2nqqGgJzAKg9YVj/DjiB7
8zbtA7/nVvC8LwtIwwGi7jY+VcbietGwNC8JOoxnTdFN4dCb1xsAcTqzt8p/NXHE
HhwGIG9YGaMoZzvwtfUGc6wrOeqxLvInq2g6e0Qk5QkhBZVg7T5DV4/mvXfheZOR
n1mENjPNMoRONifb24PqxK91CbRtBmJGxEX8b6pDB529oU6aZxNQi6xSn1KSkCJM
SZjVaDoxPqHC4V9L3/J34Rq8H96tfMvTHvSjI3+/nrX80k9MRTkIw5LMIESfTktM
oHKmIXeYcf1yymepuwFmjEgvQ/hp0P5YWsXX3xhE+OCEoaby0AQ6FRHtSJq2y8E=
=qNAo
-----END PGP SIGNATURE-----

tvaughan.vcf

Nigel Kersten

unread,
Jan 28, 2012, 9:18:52 PM1/28/12
to puppet...@googlegroups.com
I just wanted to post to this thread to primarily encourage you all to keep brainstorming, and to make it clear that I'm paying close attention. :)



--
Nigel Kersten
Product Manager, Puppet Labs


Brian Gupta

unread,
Jan 29, 2012, 1:39:50 AM1/29/12
to puppet...@googlegroups.com
Nigel,

It frightens me a bit that I think the "correct" solution, will be to replicate what the distros are doing in Puppetforge. Basically turning puppetforge into a massive cross distro metadata repo, with very strict contribution standards and rules. This would involve strong rules for curated modules that would require manpower to vet (and to contribute the modules). 

Maybe, we might want to extend modules so that there are two namespaces, one for curated forge modules and another for local modules. (Making the "local module" namespace the default for backwards compatibility.) One example of a potential rule would be to require that official modules support a mandatory set of popular OSes. Don't allow more than one official module per package. e.g.: In the curated section of forge there will be one MySQL module. (not to get too ahead of the cart, but I envision a time when you can instantiate a MySQL service by just telling puppet to use the Forge MySQL module, puppet handles downloading and installing the module and figuring out what OS you are running and with the help of the native package management, installs and configures the basic MySQL service.) The official modules will be curated such that if there is a a common resource they need to manage that resource will be split into a separate singular dependency module, that incorporates the requirements of all dependent forge modules. (Not a bag of common resources in a single module, but rather a separate module for each shared resource.)

Maybe, I am overthinking this, but I think this is the "right" solution, that may require more resources to implement than the community has available.

That leaves us what to do about "defined". (Or was that a different thread?) In the case of defined, my group has only ever used it once in almost 4 years, but it seems from the discussions that there are others still using it. Maybe the answer is provide a real alternative first, and then go about deprecating it? We wouldn't miss it, but I could see the challenges of rewriting a codebase that depends on it, and I wouldn't want that rewrite enforced on me, without a solid alternative.

Cheers,
Brian

P.S. - No one is going to really love this solution, including myself, but that doesn't necessarily mean it shouldn't be done.




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



--


Jeff McCune

unread,
Jan 29, 2012, 11:33:43 PM1/29/12
to puppet...@googlegroups.com
On Fri, Jan 27, 2012 at 5:20 AM, Felix Frank
<felix...@alumni.tu-berlin.de> wrote:
> Jeff has made a strong point against using virtual resources in modules
> at all, causing me to shift my own views as well.
> If I understand him correctly, one of the chief problems is the high
> probability of accidental collection/realisation of such resources by
> the end user's manifest.

Yep, that's it.

-Jeff

Brian Gupta

unread,
Jan 30, 2012, 12:00:30 AM1/30/12
to puppet...@googlegroups.com
Nigel,

I just wanted to add, if we do go this route, we should work to support private "forges" (module repos) as well.

Cheers,
Brian
--


Felix Frank

unread,
Jan 30, 2012, 3:51:49 AM1/30/12
to puppet...@googlegroups.com
Hi,

On 01/28/2012 04:35 PM, Trevor Vaughan wrote:
> Drawbacks:
>
> * Requires the user to have an explicit working knowledge of all
modules and namespaces
> * Adds a lot of random logic to the code (unless it becomes a
metaparam of some sort)

You skipped the most important drawback: Commitment to parameterized
classes. The fact that there can be only one place that includes those
classes, and that this singular place must have the whole picture of
what requirements are met, is conceivably a show stopper from my point
of view.

This will work for people that have a functional ENC, I guess, but
should that be a requirement for using Forge modules?

Furthermore, how can modules hope to ever interoperate like this? If all
module classes get parameterized, it will be outright impossible for one
module to ever include another module's classes.
Say module A includes class B::C. As soon as a user installs module A in
addition to B, they have to clean their manifests of inclusions of B::C.

On 01/29/2012 07:39 AM, Brian Gupta wrote:
> It frightens me a bit that I think the "correct" solution, will be to
> replicate what the distros are doing in Puppetforge. Basically turning
> puppetforge into a massive cross distro metadata repo, with very strict
> contribution standards and rules. This would involve strong rules for
> curated modules that would require manpower to vet (and to
> contribute the modules).

I honestly don't see the problem. Imagine CPAN was limited to downloads
of tarballs from the website (or even souce control checkouts). I
disbelieve it would be as significant today as it has become.
The same goes for Ruby Gems and all such systems.

As this seems to be a recurring theme: Am I wrong to compare these to
the Forge?

Sincerely,
Felix

Walter Heck

unread,
Jan 30, 2012, 5:58:10 AM1/30/12
to puppet...@googlegroups.com
Had a bunch more thoughts on this topic, and I feel I agree with
Brian: a forge with one module for every purpose, supported maybe by
library modules (like linux) would be the best. It would mean
consolidating the 17 ssh modules that exist now into one all
encompassing ssh module. In the case of SSH that is totally doable,
but consider the 300 gazillion different use cases of apache and
things get interesting. I think this would mean we'd have appointed
maintainers for each module, and if possible a puppetlabs provided
jenkins install (with community provided jenkins slaves?) that would
do the CI testing every night. That would then make it possible to
have an auto-updated overview for each module of the latest supported
OS's for puppet, and wether that module passes tests for that OS.

This seems like a massive undertaking from where we are now, but it
would in the end make all of our lives a ton easier (one trusted
source for good high quality modules) and reduce the 'problem' of
inter-module dependencies to a minimum. Of course it still exists for
in-house applications that are being puppetised, but it would already
mean the world if they would be able to depend on what the public
trusted modules define.

I personally like the way the drupal module projects work: anyone can
start a project, but they are all hosted on the drupal.org site within
drupal.org version control, and they have teams of code reviewers
maintaining integrity of the module base that lives on drupal.org.

cheers,

Walter

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

--

Trevor Vaughan

unread,
Jan 30, 2012, 6:55:29 AM1/30/12
to puppet...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

It is annoying to have everything in a single place that defines the state of your nodes but, as you point out, this seems to be the model if you're
using an ENC and that seems to be the recommended practice across the mailing list for any sort of scale.

But, you don't need a functional ENC to make this work, you simply need to have everything defined at the top level whether it be via node, higher
level class, or ENC.

The main issue here seems to be modules that are trying to be "too helpful" from my reading of the mailing list.

It seems that many would like this to be an anti-pattern:

class foo {

if not defined(Class['bar']) {
include 'bar'
bar::baz { ... }
}
}

include 'foo'

Instead, you should be less helpful, and do the following:

include 'foo'
include 'bar'

bar::baz { ... }

So, instead of doing something like, say, setting up IPTables in your module (thus creating a cross-module dependency), you should do all of this in
one monolithic place at the node level or a higher level aggregation class level.

While this keeps your modules clean, it seems like a lot more effort to maintain since the module for nginx should really know what ports it's using
and know how to set up its own firewall rules.

So, the tradeoff is an ENC vs. a large collection of cluttered classes at the top level to make sure you don't have cross-module dependencies.

I'm not sure if either is better (or if either is any good at all) but they're both functional.

The ability to tag modules as requiring other modules of a particular version (ala CPAN, Gems, everything else....) would solve this issue as Puppet
would be able to check to make sure that you have the correct version of the modules installed prior to compiling the catalog.

Trevor

- --

Trevor Vaughan
Vice President, Onyx Point, Inc.
email: tvau...@onyxpoint.com
phone: 410-541-ONYX (6699)
pgp: 0x6C701E94

- -- This account not approved for unencrypted sensitive information --
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iQEcBAEBAgAGBQJPJoUxAAoJECNCGV1OLcyp7igH/0rroAjC8Ewc9Aw2bdE7gO0N
0KfvzYCTZtJLFTBeNIAErliWd9iR5W84H0j8KJGjPg18qcRcDHjC/hnf5+GV8lIS
6kG3EgwYwyDg8Xc0qAbWubJv7bJ29X4Fc8CCHkq13CkXFM/OqnKpUbXA6X6+o5a/
Hv5Z6WXQjPC7uCupwyqktkjj5sjwvdgniSvKsj6EK3bhGRyMsvJAzmDjucwcRNsM
vz6IG05aFJrYTUp0rZzTJf/HjIPgmD90puoXXSa/RVQnsb3WSw0AwYe1jBAHWap4
pDw+F2qrMdwc9XgQv4ZFFNp/A1OCFh21uW3B1D7XjM+U3QRpmXTKhX71lcqbX08=
=XRv1
-----END PGP SIGNATURE-----

tvaughan.vcf

jcbollinger

unread,
Jan 30, 2012, 10:56:01 AM1/30/12
to Puppet Users


On Jan 30, 2:51 am, Felix Frank <felix.fr...@alumni.tu-berlin.de>
wrote:
> Hi,
>
> On 01/28/2012 04:35 PM, Trevor Vaughan wrote:> Drawbacks:
>
> >  * Requires the user to have an explicit working knowledge of all
>
> modules and namespaces>  * Adds a lot of random logic to the code (unless it becomes a
>
> metaparam of some sort)
>
> You skipped the most important drawback: Commitment to parameterized
> classes. The fact that there can be only one place that includes those
> classes, and that this singular place must have the whole picture of
> what requirements are met, is conceivably a show stopper from my point
> of view.


+1


> This will work for people that have a functional ENC, I guess, but
> should that be a requirement for using Forge modules?
>
> Furthermore, how can modules hope to ever interoperate like this? If all
> module classes get parameterized, it will be outright impossible for one
> module to ever include another module's classes.
> Say module A includes class B::C. As soon as a user installs module A in
> addition to B, they have to clean their manifests of inclusions of B::C.


Right. The approach depends on using class parameters to pass
requirements, but it depends on classes being *un*parameterized to
apply those requirements -- catch-22. There is a solution to that
particular problem, however: define requirements via external
[meta]data instead of via parameters.

That's not to say I like the proposal, however. I don't. In fact I
think it's completely inside-out. Classes should *know* what other
classes they depend upon; they should not need to be told.

Moreover, I don't see what is gained by passing requirements as
parameters rather than, say, having the user separately declare
optional classes that carry the same information. For example,
instead of a structure requiring this:

class { 'foo': reqs => ['bar'] }

it should be possible to structure a module's classes so that you can
do this instead:

include 'foo::bar'
include 'foo'

(perhaps in the opposite order).


> On 01/29/2012 07:39 AM, Brian Gupta wrote:
>
> > It frightens me a bit that I think the "correct" solution, will be to
> > replicate what the distros are doing in Puppetforge. Basically turning
> > puppetforge into a massive cross distro metadata repo, with very strict
> > contribution standards and rules. This would involve strong rules for
> > curated modules that would require manpower to vet (and to
> > contribute the modules).
>
> I honestly don't see the problem. Imagine CPAN was limited to downloads
> of tarballs from the website (or even souce control checkouts). I
> disbelieve it would be as significant today as it has become.
> The same goes for Ruby Gems and all such systems.
>
> As this seems to be a recurring theme: Am I wrong to compare these to
> the Forge?


Yes and no.

On one hand, clearly there are structural similarities between, say,
Puppet modules and Perl modules. The Forge serves a similar role for
the former to the role CPAN serves for the latter. I can totally see
the usefulness of tools for automatically installing and removing
Forged Puppet modules.

On the other hand, there are several problems with adopting such an
approach to solve the Puppet module dependency issue, among them:

1) The Forge doesn't address the problem AT ALL as it now stands. The
relevant resource that package management systems offer is not the
packages themselves (which the Forge has), but rather the metadata
database that each system relies on. Defining, building, and
maintaining such a database would be a massive and ongoing endeavor,
quite separate from developing and managing the Puppet and module
codebases.

2) A package management system would only work around the problem, not
solve it, and then only among modules from the same repository.

3) In fact, it would not really be the package management system
itself that made anything work. Instead, authors of Forge modules
would still need to find some way to make their modules interoperate,
and Forge curators would need somehow to ensure that they did. All
the management software would really contribute is a dependency-aware
ability to automatically install/remove packages. The system overall
would provide a promise that the particular modules it contains all do
work together, but that's altogether different from a means by which
anyone -- the Forge or otherwise -- can fulfill such a promise.


John

Nigel Kersten

unread,
Jan 30, 2012, 11:59:34 AM1/30/12
to puppet...@googlegroups.com
On Sat, Jan 28, 2012 at 10:39 PM, Brian Gupta <brian...@brandorr.com> wrote:
Nigel,

It frightens me a bit that I think the "correct" solution, will be to replicate what the distros are doing in Puppetforge. Basically turning puppetforge into a massive cross distro metadata repo, with very strict contribution standards and rules. This would involve strong rules for curated modules that would require manpower to vet (and to contribute the modules). 

It's worth pointing out here that a massive cross distro repository of modules doesn't necessarily require strict contribution standards and rules.

There are other options to ensure that high quality modules are available other than creating a high barrier to entry for the modules themselves. Exposing community opinion on modules and success rates of people deploying them can help us all achieve the same goal.

What kind of a repository do we want?
 

Nick

unread,
Jan 30, 2012, 12:28:49 PM1/30/12
to puppet...@googlegroups.com
On 27/01/12 18:27, jcbollinger wrote:
>> Anyway, I need to get back to work, I'll try to say more in a later email.
>
>
> That would help me to determine what I think about the idea. As it
> is, I suspect I don't quite understand what you are hoping to
> accomplish with it.

Ok, here's a couple of examples. Apologies for the length.


1. Packages

Let's say I'm writing a module called foo.

It uses a package called 'libfoo'.

Therefore I want to make sure this package is installed before the module's
configuration is applied. I use the pattern where I define separate classes to
handle install and config, and chain them together:

class foo::install {
# Make sure libfoo is installed
package { 'libfoo': ensure => present }
}

class foo::config {
# adjusts foo.conf
# ....
}

class foo {
include foo::install
include foo::config

Class[foo::install] -> Class[foo::config]
}


Bingo! Works fine. Until I want to add another 3rd party module which needs
libfoo. Now I have to move the package declaration out to a shared class and
update both modules to fit: maybe the other module wants a specific version,
other install_options, or whatever.

Sounds simple in a short example like this, except, that:

- I've been forced to customise what should be an 'off the shelf' module,
- I have to figure out what the shared class/module should say
- and fix an arbitrary name for it
- I've therefore hard-wired strong coupling in both to my shared class
- I've added potential for refactoring breakage
- and more of the same sort of problems when scaling up

Genuinely reusable modules seem nearly impossible to write as it stands. If I
want to publish my module on Puppet Forge, then the shared::libfoo module must
be published too. Except it might not agree with other published modules' ideas
about what a shared::libfoo should declare, or even be called, and so it is not
typically re-usable without refactoring.

Or, I don't publish it, leave a dangling dependency on a class called
shared::libfoo. I am still hard-wiring a name, but not what it does. I have to
tell users the name the module they must define, and a list of requirements to
put in it on our behalf.

Or, I just don't define anything about libfoo except in the documentation.
Which seems the most practical thing to do, assuming that things break
intelligibly if libfoo is absent, but this really amounts to giving up and
moving on.

Or, maybe there currently are better ways than all the above - but if so I'm
unclear what.


Now imagine we could simply assert a requirement on a package, without actually
managing it. For the sake of this example, I'll invent a syntax for
"Assertions" similar to that used for Virtual Resources which use an '@' sigil
prefix. I'll arbitrarily use a '+' instead:

+package { 'libfoo': ensure => present }


This just means "ensure libfoo is installed". It changes nothing about the
target system. It does not mean "go and install the 'libfoo' package". It does
not mean "I own the 'libfoo' package resource, and I'll fight anyone who tries
to say otherwise".

Therefore, this type of assertion can be repeated multiple times in different
modules. Possibly in slightly different ways - with extra attributes, etc.
Puppet should just check they are all consistent, and fail if they aren't, or if
the net requirements are not met. I don't know enough about Puppet internals to
say for sure, but as described in my previous email: because the Assertion
changes nothing, I hope this would be relatively easy to implement.

Now I can write my module using an Assertion instead:

class foo::install {
# Make sure libfoo is installed
+package { 'libfoo': ensure => present }

# ...
}

...and I no longer have to find the common ground between modules which use
libfoo, and/or modify the modules to use the shared declaration.

Also, we have lost an explicit dependency on a shared module arbitrarily called
'shared::libfoo' which merely declared:

package { 'libfoo': ensure => present }

So I no longer need to publish this shared module and either dictate to, or
negotiate with with potential users of my module about the intersection of our
requirements. Nor do I need to omit this requirement entirely (which might be
the only practical alternative).

Yet I am still checking the prerequisites are there.

Of course, I may still have to create a package which actually does the
appropriate package install. Or maybe not? Perhaps my provisioning system does
that for me, and I can skip that step? Either way, the knowledge that my system
is still checking the prerequisites are there.

If my prerequisites are missing, I would hope Puppet would give helpful errors
showing what needed what, and I can add a declaration to install the right
packages in a top-level "glue" class. But means we can avoid hard-wiring
arbitrary module names into the component modules.

In summary, this would be simpler and more effective than any existing Puppet
pattern I know about.


2. Creating user accounts

Another example, which was the topic of my earlier post "constraint checking".
Say I want to create a custom resource which sets up user accounts for me in a
manner of my choice.


define user_account(
$name,
$home,
$shell,
$passwd,
$uid,
$gid,
$groups,
) {
# I want to validate $home, $shell, $groups exist and are usable...

# This is a classic case where one is tempted to use this anti-pattern
# to define something usable if it doesn't exist:
if !define(Group[$gid]) {
group { $gid: ensure => present }
}

# If I can't do that, perhaps I can just depend and hope it's picked up
# elsewhere?
require File[$shell]

# ... except this can't say anything about $shell, like
# "it must be executable".


# .... do other stuff here ....

# Now define the user resource which creates the user
# (but in my tests, does not seem to check the requirements
# exist.)
user {$name:
ensure => present,
home => $home,
shell => $shell,
passwd =>
uid => $uid,
gid => $gid,
groups => $groups,
}
}

Imagine we could use Assertions as described above. Validating the parameters
is now straightforward:

define user_account(
$name,
$home,
$shell,
$passwd,
$uid,
$gid,
$groups,
) {
# Ensure $home, $shell, gid, $groups exist

# We want to own this one
group { $gid: ensure => present }

# These are shared
+group { $groups: ensure => present }

# This is shared
+file { $shell:
ensure => present,
mode => '0744', # ...if only I could merely say "a+x" here
}

# ... do other stuff ...

# Now define the resource:
user {$name:
ensure => present,
home => $home,
shell => $shell,
passwd =>
uid => $uid,
gid => $gid,
groups => $groups,
}
}

And as above:

- only User[$name] or Group[$gid] will ever conflict (which is what we want)
- there are no shared module dependencies forced into existence
- we are not risking silly mistakes like shell => '/bin/bqsh' or
groups => ['weel']


There's more I could say, but I hope this gives the basic idea.


Cheers,

N

Felix Frank

unread,
Jan 30, 2012, 12:48:22 PM1/30/12
to puppet...@googlegroups.com
Hi,

thanks for your elaborate design sketch.

Sorry for limiting my quote severely.

On 01/30/2012 06:28 PM, Nick wrote:
> +package { 'libfoo': ensure => present }

Is this different from John's "constraint" proposal?

To me this didn't become clear: Does the manifest still need to declare
an actual package { "libfoo" } somewhere, or is this implied by at least
one assertion regarding any of its parameters?

Of the latter is the case, then this is not different from just allowing
puppet to consume multiple declarations of the same resource, along with
all the oft-discussed difficulties.

If instead, there still is that one central resource declaration
somewhere, I'm pretty sure this is the same as constraints.

Which is probably a really neat idea.

Cheers,
Felix

Nick

unread,
Jan 30, 2012, 4:28:10 PM1/30/12
to puppet...@googlegroups.com
On 30/01/12 17:48, Felix Frank wrote:
> Hi,
>
> thanks for your elaborate design sketch.
>
> Sorry for limiting my quote severely.
>
> On 01/30/2012 06:28 PM, Nick wrote:
>> +package { 'libfoo': ensure => present }
>
> Is this different from John's "constraint" proposal?

It did sound similar, yes - but unless I misunderstand it, not identical. For
example, I don't understand how Constraints would avoid the problems with
unifying resources that Nan mentioned.

John's example appeared to be wrapping an existing Resource with something which
puts constraints on it, i.e. a layer on top of "Resources". It implies a regular
Resource to wrap somewhere.

Whereas what I had in mind was something which in principle at least, was more
basic than a Resource. With an "Assertion" there is nothing being managed, or
mutated, by definition. It defines conditions on a resource (lower-case 'r')
which can be checked, and merged, but doesn't imply that any Resource
(upper-case 'R') need to be declared. It's quite possible that one wouldn't
bother, if you don't need to manage or mutate anything.

So Resources (upper case 'R') could be thought of as extensions to Assertions
which also supply rules to mutate a system's state, should the conditions of the
Assertion not be met, so that the conditions *are* met.

> To me this didn't become clear: Does the manifest still need to declare
> an actual package { "libfoo" } somewhere, or is this implied by at least
> one assertion regarding any of its parameters?

To be explicit: if an Assertion "+package { libfoo: }" is declared, it just
means "libfoo must be installed for this manifest to work". I don't think it
needs to mandate a declaration of a full-blown "package { libfoo: }" somewhere.

In fact, I can probably imagine circumstances when something might be invoked
which indirectly takes care of the "libfoo" package (or file, or whatever) - and
then being forced to manage the "libfoo" package in Puppet just because you want
to assert its presence could be a liability.


N

Felix Frank

unread,
Jan 31, 2012, 4:01:40 AM1/31/12
to puppet...@googlegroups.com
Hi,

On 01/30/2012 10:28 PM, Nick wrote:
> It did sound similar, yes - but unless I misunderstand it, not identical. For
> example, I don't understand how Constraints would avoid the problems with
> unifying resources that Nan mentioned.

as far as I understand, there is no need to merge anything. The catalog
will or will not contain a certain resource. If existing, the resource
will have a certain set of parameteres and metaparameters. Each
constraint can be individually compared to this state. If a constrained
resource does not exist, or any of its (meta)parameters deviate from
what the constraint defines, the catalog is no longer valid.

The beauty of this design is that the language is very expressive and
all validation can be done by the master.

Err, right, John? :-)

> John's example appeared to be wrapping an existing Resource with something which
> puts constraints on it, i.e. a layer on top of "Resources". It implies a regular
> Resource to wrap somewhere.
>
> Whereas what I had in mind was something which in principle at least, was more
> basic than a Resource. With an "Assertion" there is nothing being managed, or
> mutated, by definition. It defines conditions on a resource (lower-case 'r')
> which can be checked, and merged, but doesn't imply that any Resource
> (upper-case 'R') need to be declared. It's quite possible that one wouldn't
> bother, if you don't need to manage or mutate anything.

Ah, so you'd have the agent verify if all assertions (which need to
appear as first-class citizens in the catalog) hold true, and otherwise
fail the catalog?

That strikes me as very elegant indeed.

How will situations be handled where assertions won't hold true until
parts of the catalog have been applied?

> So Resources (upper case 'R') could be thought of as extensions to Assertions
> which also supply rules to mutate a system's state, should the conditions of the
> Assertion not be met, so that the conditions *are* met.

Let's not alienate the community by declassing the proven and beloved
Resources ;-) but I've got to say, this idea does hold merit.

So does the constraint idea. Something tells me that both might be of
benefit, but I'm afraid of years of user confusion to come when everyone
is tasked with understanding the difference between the two and to
decide when to use which.

If we need to take a pick, there's two things I'd have to say for
constraints:
1. They're more closely tailored to the problem at hand.
2. They're stronger in chorus with what puppet is today.

Assertions would probably widen the borders of what's possible with
puppet (and how easy it is), and they would allow/require us to part
with some paradigms. I'm torn whether we want this sooner than seeing
the multiple declaration problem sorted out in a less intrusive way.

Cheers,
Felix

Nick

unread,
Jan 31, 2012, 5:13:40 AM1/31/12
to puppet...@googlegroups.com
On 31/01/12 09:01, Felix Frank wrote:
> Ah, so you'd have the agent verify if all assertions (which need to
> appear as first-class citizens in the catalog) hold true, and otherwise
> fail the catalog?
>
> That strikes me as very elegant indeed.
>
> How will situations be handled where assertions won't hold true until
> parts of the catalog have been applied?

I'm not familiar enough with Puppet's internals to answer that very confidently.
My guess is that one might be able to express requires/before relationships
between Assertions and Resources in order to enforce things like this.

The main implication of that would be to restrict the freedom to assume that
assertions can be applied in any order, because the agent's application of the
catalog would need to be split into a sequence of "assertion" and "mutation"
steps. An Assertion must then not be moved outside the part of the sequence
assigned to it.

N

jcbollinger

unread,
Jan 31, 2012, 11:38:28 AM1/31/12
to Puppet Users


On Jan 31, 3:01 am, Felix Frank <felix.fr...@alumni.tu-berlin.de>
wrote:
> Hi,
>
> On 01/30/2012 10:28 PM, Nick wrote:
>
> > It did sound similar, yes - but unless I misunderstand it, not identical.  For
> > example, I don't understand how Constraints would avoid the problems with
> > unifying resources that Nan mentioned.
>
> as far as I understand, there is no need to merge anything. The catalog
> will or will not contain a certain resource.


That is correct. Constraints do not have their own separate Resource
instances. Instead, they use resource type and title, or perhaps a
resource reference, to identify a resource to which they apply.
Traditional Puppet rules are in effect -- there is never more than one
Resource per catalog for each (type, title) pair.


> If existing, the resource
> will have a certain set of parameteres and metaparameters. Each
> constraint can be individually compared to this state. If a constrained
> resource does not exist, or any of its (meta)parameters deviate from
> what the constraint defines, the catalog is no longer valid.


And that's getting around to the interesting part. Constraints could
leave it there, and that would be a good and useful solution.
Something like that could even be implemented in 2.7, as it introduces
no incompatibility whatever.

I am proposing that constraints might go even further, however, in one
or both of these ways:

1) Where the resource definition to which one or more constraints
apply does not explicitly specify a value for a constrained parameter
or property, applicable constraints are used to *choose* a value
(rather than allowing that property to take a default value). This
would help with the problem of writing compatible resource definitions
in the first place. Example:

Somewhere:
----
package { 'foo':
provider => 'yum'
}


Somewhere else:
----
constraint { 'my-package-foo-ensure':
resource => Package['foo'],
property => 'ensure',
forbidden_values => ['absent', 'purged'],
preferred_values => 'latest'
}


Yet another other place:
----
constraint { 'your-package-foo-provider':
resource => Package['foo'],
property => 'provider',
allowed_values => 'yum',
}


The definition of Package['foo'] explicitly specifies the 'provider'
property, so Constraint['your-package-foo-provider'] only validates
it, failing catalog compilation if the value does not satisfy the
constraint. The definition does not explicitly specify a value for
the 'ensure' property, however, so Puppet *chooses* one that is
mutually agreeable to the relevant constraints. In this case that
would work out to 'latest'. If there is no mutually agreeable value
then catalog compilation fails.


2) Extending (1), if constraints are declared for a resource that is
not otherwise itself declared for a node, then Puppet synthesizes a
declaration, using the applicable contraints to set values for all
constrained properties. This could be considered a create_resources()
on steroids.


These would make Constraints much more useful and powerful because
they lessen the burden on module users to write suitable resource
declarations. Instead of merely testing after the fact whether an
appropriate Resource was declared, item (1) would allow modules to
*collaborate* to ensure that suitable Resources are declared, without
requiring those modules to have any knowledge of one another. Item
(2) would further reduce the burden on module users by alleviating any
need for them to explicitly declare modules' needed resources
anywhere, though they could provide such declarations if they wished,
subject to the same rules that apply already.


> The beauty of this design is that the language is very expressive and
> all validation can be done by the master.
>
> Err, right, John? :-)


Indeed, the language is not only expressive, but also completely
compliant with current DSL syntax. I'm not sure how magical the
implementation of the Constraint [meta]resource type would need to be,
but at least in principle I think it fits very cleanly into Puppet's
model. Furthermore, because all the work is done on the master,
*nothing* about the agent or any provider needs to change. Probably
no resource type implementation needs to change, either.


> > John's example appeared to be wrapping an existing Resource with something which
> > puts constraints on it, i.e. a layer on top of "Resources". It implies a regular
> > Resource to wrap somewhere.
>
> > Whereas what I had in mind was something which in principle at least, was more
> > basic than a Resource.  With an "Assertion" there is nothing being managed, or
> > mutated, by definition. It defines conditions on a resource (lower-case 'r')
> > which can be checked, and merged,  but doesn't imply that any Resource
> > (upper-case 'R') need to be declared.  It's quite possible that one wouldn't
> > bother, if you don't need to manage or mutate anything.
>
> Ah, so you'd have the agent verify if all assertions (which need to
> appear as first-class citizens in the catalog) hold true, and otherwise
> fail the catalog?


And here you have hit on some of my main issues with Assertions, as I
understand that proposal:

1) Assertions are tested by the agent (or else they do nothing that
constraints do not also do), so they need to be represented in the
catalog. This makes them no lighter-weight than ordinary Resources as
far as I can tell.

2) Supposing that Assertions are indeed tested by the agent, they
defer catalog failures to the agent, which makes module interoperation
(the issue we're trying do deal with) harder to test.

3) Assertions leave the burden on module users to provide resource
declarations that are agreeable to all modules.


> That strikes me as very elegant indeed.


I'm not persuaded that it is a broadly useful capability. I
acknowledge that there may be cases where one wants the agent to
perform a test without changing anything. At present, one would
probably use an Exec for the purpose. I tend to think, however, that
such cases usually reflect module design shortcomings.

The Puppet paradigm is to tell the agent how a node should look, then
let the agent make it so. Why, then, would it be desirable to give
the agent contradictory node descriptions? Why would it be desirable
to give the agent a node description, but not allow it to make the
node meet the description? How would either capability further the
goal of module interoperability? Especially, why should an
interoperability solution that relies on such behaviors be preferred
to one that does not?

Assertions would be an interoperability aid for one of the same
reasons that Constraints would be: they would allow modules to express
resource requirements without actually declaring resources. This is a
better solution than merging multiple Resource declarations would be
because modules' requirements are expressed precisely, and also
because there is no change to the invariants of Puppet's data model.
In these respects, however, Assertions offer nothing that Constraints
do not also offer.


> How will situations be handled where assertions won't hold true until
> parts of the catalog have been applied?


That, too. Assertions would need their own support for resource
relationships to adequately address that issue. At this point they
don't look lightweight at all to me; rather they look like a full-
blown new [meta]resource type (as Constraints also would be).
Implementation of Assertions separate from Resources, as Nick
suggests, would be tricky. The Assertion provider would need to
somehow hook into all other providers, including custom ones, to test
whether assertions were satisfied.


> > So Resources (upper case 'R') could be thought of as extensions to Assertions
> > which also supply rules to mutate a system's state, should the conditions of the
> > Assertion not be met, so that the conditions *are* met.
>
> Let's not alienate the community by declassing the proven and beloved
> Resources ;-) but I've got to say, this idea does hold merit.


I think the best chance of integrating something like Assertions into
Puppet is to flip that around: assertion nature could be an attribute
of (some) Resource instances. Consider its similarities to the
effects of the 'audit' metaparameter, for instance. Only in that way
can I reconcile Nick's characterization of Assertions as "lightweight"
and "low-level" with the apparent design and implementation
requirements.


> So does the constraint idea. Something tells me that both might be of
> benefit, but I'm afraid of years of user confusion to come when everyone
> is tasked with understanding the difference between the two and to
> decide when to use which.
>
> If we need to take a pick, there's two things I'd have to say for
> constraints:
> 1. They're more closely tailored to the problem at hand.
> 2. They're stronger in chorus with what puppet is today.
>
> Assertions would probably widen the borders of what's possible with
> puppet (and how easy it is), and they would allow/require us to part
> with some paradigms. I'm torn whether we want this sooner than seeing
> the multiple declaration problem sorted out in a less intrusive way.


There may well be a better solution for module interoperability than
Constraints, but Assertions (as I understand them) are not it. I can,
however, imagine using Assertions similarly to how I use the like-
named features of C and Java -- to validate assumptions and internal
consistency in catalogs.


John
Reply all
Reply to author
Forward
0 new messages