On Wednesday, June 18, 2014 6:07:02 PM UTC-5, henrik lindberg wrote: > > On 2014-18-06 22:44, John Bollinger wrote: > > > > 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). > >
I'm thinking about something more restricted than it may seem I've suggested: an ability only to reflect which *classes* are in the catalog, in a context and time where no new classes can be added (but resources can). That characterization draws on my position that classes should not be considered resources, at least at DSL level, but you can say instead "(but resources other than classes can [be added])" if you prefer. > > 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. > > The idea was that this limited form of reflection escapes evaluation-order dependencies because it draws on information that will not subsequently change -- to wit, which classes are in the catalog. That's the reason for preventing new classes from being added. Now that I think about it, though, I'm no longer sure that would be sufficient. I think this would introduce a new form of evaluation-order dependency involving the effects of collectors, since the full set of resources selected by any given collector can be determined reliably only after all resources that will be added to the catalog have been added, at least virtually. On the other hand, maybe this problem isn't new -- it seems like collectors can already suffer from this problem on their own. And I think any solution to that problem creates its own problems. Consider this artificial example: define loop::a ($index = 0) { @loop::a { "a$index": index => $index + 1 } } @loop::a { 'start': } Loop::A<| |> Obviously, you shouldn't write infinite loops, but my point is that on the one hand, whether that is in fact an infinite loop depends on the evaluator implementation, but on the other hand, an evaluator for which it is NOT an infinite loop does not reliably collect all resources that should be collected. (And on the third hand, it might not even be possible to collect all the resources that should be collected.) > 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. > > But if a class is added as a result of that mechanism, then it turns out that the predicate wasn't evaluated at the end of catalog production after all. That's not an issue for just one such constraint, but as soon as you have two or more you are right back to having an evaluation-order problem. And consider also the issue with collectors that I raised above -- I think that probably applies here, too. This adds a much asked for feature; autoinclude. Inclusion in the > catalog by just adding a module to the modulepath. As opposed to adding "include 'mymodule'" to the top level of the starting-point manifest? Or adding a file containing that to the starting-manifest directory? What's hard about that? The latter could even be arranged as part of module installation. Or why not have a separate module path for autoinclude modules? Users then select autoinclude behavior by where they put the module -- or even by where they put a symlink to the module. I think simple autoinclude is a relatively tractable problem, but I guess you're looking at conditional autoinclusion. Honestly, I'm not very comfortable with that. Moreover, I'm inclined to think that whatever framework might be devised for evaluating which of the available modules to "auto"include would be no simpler, better, or easier than what we already have. > (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. > > I think you're saying there needs to be a way to set up dependencies between classes and resources added to the catalog during the "normal" evaluation phase and those added as a result of this hypothetical coda. That supposes, I guess, that those cannot be declared either during the normal evaluation pass (e.g. via a collector) or during the additional one. I'm uncertain whether that's true. > 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). > > Basically that's a way to constrain evaluation order, right? Such a mechanism would indeed address the core problem, and conditional evaluation can falls out as a special case. It might even fit in easily with the current (future) evaluator, as it seems like it has some parallels with 'include'. So, yes, this seems like a promising direction to explore. John -- 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/fc18d6cc-f3f3-4f67-b5f8-3dcf84995b79%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.