On 2014-18-06 22:44, John Bollinger wrote:


On Wednesday, June 18, 2014 4:22:16 AM UTC-5, Trevor Vaughan wrote:

    Hi Felix,

    Yes, I'm very aware that the current recommendations are to very
    cleanly model your system to be able to non-ambiguously define your
    catalog.

    However, there have always been arguments that the Puppet language
    should be declarative throughout which makes for more maintainable
    and intuitive manifests overall.

    If you go look for posts surrounding this subject, I'd say that the
    consensus is because reflection just doesn't work, not because it
    shouldn't be there.



To the extent that I get to say what the consensus is -- and I have been
outspoken enough on the topic to suppose I have helped shape that
consensus -- I'd say it's that most anything dependent on evaluation
order does not work reliably, and reflection shouldn't be there because
it cannot be relieved of evaluation-order dependencies.  (Sorry, Henrik,
if I'm getting my different orders mixed up.)  To put it another way,
I'd say reflection *can't* work.

Agree, can never be made reliable. (And evaluation order it is).

If, however, some constrained context could be established in which to
perform reflection, then perhaps some form of reflection could be
introduced.  For example, it might be possible to inspect which classes
are in the catalog from a construct wherein, or at a time when, no
further classes can be added.  Adding bona fide resources would be
allowed (else what would be the point?), but they would need to actively
be prevented from adding classes.

    Frankly, it can solve a LOT of problems for you and make the system
    designer's life a lot easier.



It does not solve /any/ problems if it doesn't work reliably, and
general reflection cannot work reliably.

Agree again.

    Scenario 1: A requires B if C but not otherwise.

    If I don't have reflection in this case, then I have to have
    documentation that says, hey, if you put A and C together on a
    system, remember to add B! This is prone to error and is something
    that the code should just "take care of" for me. I'm aware that you
    could use a role/profile model to take care of this but you're just
    making people write extra code for no really good reason except that
    the language doesn't actually address this issue. (I'll rant about
    the irritation of figuring out what code is doing in a role/profile
    system later).

    Scenario 2: A requires B if module B is present but do something
    else otherwise.

    This one is more about system introspection I suppose but we have
    all of these lovely Module Forge description files and we can't use
    them in the language! I would love to be able to do the following:

    if $::module_mysql <= '1.2' { include 'mysql' } else { service {
    'mysql': ensure => running, enable => true } }

    You can then also use $::module_mysql to set up automatic Hiera
    hierarchies that run from version down to a default
    (hieradata/mysql/1.0, hieradata/mysql/0.9, hieradata/mysql/default)
    and life is magic and wonderful when modules on the forge change.

    Scenario 1 is obviously what we're referring to in this thread and I
    think, with the way most cloud component architectures seem to be
    designed, the burden is being placed on the system user as opposed
    to the system designer in too many cases. And, frankly, some times
    you just need to get something working and you hit a point where you
    realize that you'd have to redesign the system to avoid using
    reflection and you just don't have time for that or it is going to
    add a LOT of unnecessary code.



Puppet already has a tool for separating the details of a resource from
the decision about whether to include it in the catalog: virtual
resources.  Those decisions can be tied to whether a given class is
declared by having that class realize or (especially) collect the
resources.  In many cases that can be done via a wrapper class or
definition if the target class itself must not be modified.

Here's an idea: Collectors are already processed very late, so maybe
they are late enough -- or could be /made/ late enough -- to provide the
kind of context needed for safe reflection.  One might then write
something along these lines:

Firewall::Rule<| title == 'whatever' and defined == 'firewall' |>
@firewall::rule { "whatever":
   # ...
}

Of course, the collector wouldn't need actually to be close to the
resource declaration, and it might be more general than just one
resource, but it could be right there with the resource declaration to
provide something much like an if defined() effect.

The only thing this does is to move the problem to a new "phase".
Since the purpose is to add resources, naturally you can add defined types, which in turn creates many resources. The set of rules then operate on a moving target (constraint solving).

For that to be safe though, it would be necessary to prevent the
collected resources from adding classes to the catalog, probably by
failing the catalog if any of them try to do so.  That restriction might
be reserved for when the 'defined' key is used in the collector's select
expression, provided that such collectors were processed after all others.

I don't think we absolutely have to protect classes from being included. In a way they are just resources only singletons.

I am thinking that a module can specify constraints as a predicate (either on other modules being present, other classes, or resources, or indeed on some abstract "capability"), and if the predicate becomes true at the end of the catalog production, then it includes one class from the module.

This adds a much asked for feature; autoinclude. Inclusion in the catalog by just adding a module to the modulepath. (This is the reverse dependency I was talking about earlier).

In all of these cases, some kind of constraint solver ability is needed across the new feature, queries and the forming of dependencies (since you most certainly need to also have control over the order in which the "injected" classes/resources are later applied.

The other approach I can think of (I wrote about it earlier) is that you make explicit statements about things that must have been evaluated before "you" can be evaluated (i.e. "I need these Promises fulfilled / defined before I can say what I want", and modules are given the ability to define such promises. This (as John also points out) requires a context in which a resolution of available Promises can take place reliably). Maybe a Promise is nothing else but a class, but that you can tie your evaluation to the outcome to the evaluation of one or many other classes. (This is an area I hope to be able to work on ideas after we have released puppet 4).

Regards
- henrik
--

Visit my Blog "Puppet on the Edge"
http://puppet-on-the-edge.blogspot.se/

--
You received this message because you are subscribed to the Google Groups "Puppet 
Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to puppet-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/puppet-dev/lnt621%24n91%241%40ger.gmane.org.
For more options, visit https://groups.google.com/d/optout.

Reply via email to