* Make application of defaults eager so that when a resource is
instantiated, it will immediately get the registered and visible
defaults (for missing attributes), at *that* point of time in the
evaluation. This means that defaults become imperative (like variable
assignment).
* When resource defaults are applied eagerly they are also available
when doing collection (instantiation is naturally done before collection
- otherwise there is nothing to collect).
* There are edge cases where collection can be made at a point where all
resources that the query matches (will match) have not yet been created
(i.e. if the virtual resources are instantiated after the query is
evaluated). There seems to be logic that tries to find everything
(lazily at the end), but it is uncertain that it actually works
correctly in all cases. What we propose for the defaults have no effect
on this, it only changes what the resource attributes are when collecting.
* We want to change the function 'defined' to return the state of the
resource instead of just a boolean. Currently the concept of being
"defined" is vague, the function returns true for all kinds of resources
(virtual and exported) as well as those that are included in the
catalog. We want to return some kind of status (that is "truthy" to make
it backwards compatible), but that also tells if the resource is
actually realized (in the catalog).
(We agree that using the defined
function is bad practice, but it is required to support certain
use-cases that would otherwise be very painful/impossible to handle).
* The File[foo][mode] syntax for lookup of a resource attribute will be
more sane, as it will correctly produce the value at that point in time
in the evaluation. It is either the value (explicit or default) that
will end up in the catalog for a realized resource, or the explicit or
default value of an unrealized virtual resource that *may* get a
different value when it is later realized (if realized at all).
Thus,
with a combination of being able to know if a resource is realized or
not, it is possible to also be certain that the value is the value that
will end up in the catalog).
We believe that the construct with lazy default application was required
when dynamic scoping was available, but we are honestly not sure about
the rationale behind the current design.
There are plenty of
logged issues in Redmine regarding defaults, and collection, but that
did not help us much as they are for a variety of versions, and for when
dynamic scoping was available (plus a number of other issues where not
fixed), so it is very hard to tell which of the old issues that are
still relevant.
Need to correct myself... see below...
What we cannot predict is what value will be set for realized resources
that have an undef value for an attribute. Those are the only ones that
can be modified with a Resource Override.
If we predict the default, an override may set it to something different
than the default.
An unrealized resource may be completely overridden, so there you need
to know if it is realized or not to be able to know how well the
prediction will hold for any value.
We *are* getting rid of dynamic scoping of resource defaults. This has
already been done in 3.7 and is in effect when using the --parser
future. Defaults follow lexical scoping.
> The two cases that we are concerned about is a) the ability to lookup
> resource attribute values, and whether this should return the currently
> known default values or not. We can see it as not looking up default at
> all, or lookup the default values as known at that point in evaluation.
> (Since resource overrides are also in play, and value is actually just a
> prediction),
and b) collection of virtual resources since it is unclear
> what default values have been applied and can be queried...
John seems to have done more reading on this topic.
If it turns out to
be true that subclasses can change already set attribute values (at
least +> will do so anyways), the Foo[x][y] is already in a deep
hellhole of undefinedness and evaluation order dependency.
Hmmm ...
@file { "/tmp/fail": content => '', tag => [ 'example1', 'example2'
]; }
File <| tag == 'example1' |> { mode => 0644 }
File <| tag == 'example2' |> { mode => 0755 }
puppet (apply, 3.1.1) will create /tmp/fail with 0755. Changing the
order of the two collectors changes the mode of /tmp/fail.
> Now, the function lies - it says that virtual (unrealized) resources
> are
> also "in the catalog", which is not really true. (The concept of
> being "defined" is very loosely, uhm... defined)
I'm not saying it shouldn't be fixed (probably 4.0 is a good target to
do so; creating warnings for the "wrong" cases in 3.7). I'm just saying
that I don't see the connection to resource defaults.
>>> * In case you wonder, the ability to lookup an attribute is
>>> available
>>> server side, when the catalog is produced. Later default values set
>>> by
>>> the agent cannot (for obvious reasons) not be computed when the
>>> catalog
>>> is being compiled.
>>
>> When/how does the agent apply defaults to resources? Have you ment
>> "the
>> state the agent reads from the running system"?
>>
> yes, the agent, when the catalog is applied to the node triggers
> logic in types and providers that may "set defaults" depending on the
> state of the system where it is running.
That would be Puppet::Type#newtype#newproperty#defaults_to logic then?
I would consider types whose defaults_to implementation acts differently
on the master and the agent as Very Special[tm] and dangerous. The
default provider being a striking counter example of course.
A different idea to reduce the scope of the problem: Decouple the
defaults discussion from the bog of other undefined behaviour by moving
default setting to a very strict localized syntax:
file {
mode => 0321,
owner => weird;
"/tmp/foo": content => 'foo';
"/tmp/bar": content => 'bar';
}
could be a shorthand for setting those attributes separately on all
files in this block. That would make it immediately obvious, that those
defaults have a very limited scope and are applied before the closing
brace of the file{}. They become syntactically "atomic". They cannot
leak to other files in the same class by scrolling off someone's mind.
They cannot interfere with collection. The old syntax can be handled
separately for compatibility.
That doesn't change that all the other
mutability/evaluation-order/parse-order problems around collection and
overriding may have to be addressed, but it would decouple the problems.
Due to the dynamic scoping of them I only really use them for setting global defaults. And some mechanism for doing that is really needed.
Using them inside classes is really mostly syntactic sugar to get the code shorter. But I've also seen them used to conditionally set an attribute. So not set it to value or undef, to set it to value or not set it at all, as that has slightly different semantics. But that would also really be better to do with some sort of postfix if condition in the resource declaration.
--
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+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/lp5163%2472b%241%40ger.gmane.org.
For more options, visit https://groups.google.com/d/optout.
On 2014-03-07 22:57, John Bollinger wrote:
> Here's another idea: how about removing resource defaults altogether?
> My take on them has always been that their dynamic scope was their most
> important feature. If that's going away then what's left is just a
> minor piece of syntactic sugar, and keeping them in that restricted form
> is certain to cause more pain than than just dropping them would..
>
There are several kinds of scoping in effect. It used to be dynamic,
which if I understood this correctly meant that defaults applied from
where something was defined as well as where it was included.
We now flipped that to follow how variables are resolved, if not defined
in the same scope, it goes to inherited, and then global scope.
I agree
that this pretty much renders defaults useless as they have to be global
or repeated per class / define. It is only useful if a define or class
creates many resources.
If defaults followed lexical scope / (namespaces). Then the defaults
would apply to the namespace it is defined in, and all inner namespaces.
That would make them more useful while also safe to use. They would not
apply to something that is included.
On 2014-07-07 15:43, John Bollinger wrote:
[...] Consider:
>
> class mymodule {
> File { group => 'example' }
> }
>
> class mymodule::example {
> file { '/tmp/example': owner => 'root', ensure => 'file' }
> }
>
> include 'mymodule::example'
> include 'mymodule'
>
> File { mode => '0400' }
>
>
> What should File['/tmp/example']['group'] be? And
> File['/tmp/example']['mode']?
The evaluation will evaluate both classes before the default expression.
The resources are thus created before the default is added. If we do go
for eager defaults, then the two resources would not get an default values.
> If part of the objective is to avoid
> binding defaults late without (re)opening the door to issues around
> evaluation order uncertainty, then you can rely only on the local
> lexical scope for defaults. And yes, variable references do suffer from
> that issue already.
>
> I agree
> that this pretty much renders defaults useless as they have to be
> global
> or repeated per class / define. It is only useful if a define or class
> creates many resources.
>
> If defaults followed lexical scope / (namespaces). Then the defaults
> would apply to the namespace it is defined in, and all inner
> namespaces..
> That would make them more useful while also safe to use. They would not
> apply to something that is included.
>
>
>
> It's not clear to me how that last alternative differs from the previous
> one, nor whether it fares any better on the evaluation-order front
> relative to my example above.
If we do defaults per named scope and not do late binding, care has
to be taken to introduce entities in the right order (to get them
evaluated in the wanted order - you want to evaluate nested named
elements after outer elements. This is probably just as confusing to
puppet users as the current way.