Re: [Puppet Users] Re: Negate or uninclude a class

Felix Frank
Thu, 21 Oct 2010 08:41:55 -0000

On 10/19/2010 07:07 PM, Bruce Richardson wrote:
> On Tue, Oct 19, 2010 at 01:36:25PM +0200, Felix Frank wrote:
>>
>> Subclasses that effectively disable a class are a sound concept.
>
> Subclasses which disable a resource are a sound concept; subclasses
> which attempt to negate a claass present many problems, some of which I
> have described, none of which you hae addressed.

Well, that''s true.

>   1.  If the resources in the ldap::client class change, the
>       ldap::client::disabled will have to be changed to match.  This
>       just begs to be a source of error.

Yes. :-) Potential maintenance nightmare that cannot easily be avoided.

>   2.  The structure of the ldap::client class and it''s resources
may
>       well have to be distorted to fit this arrangement.  Some resources
>       would have to be set up quite carefully so that they could be
>       "negated".

If you have resources like "mount" in mind, I''m again
inclined to
concur. On the other hand, "mount" is a good example for a resource
where "no longer including the class" won''t do you any good
wrt.
lingering resources on the client.
But you have gone into this to some detail in the other branch with
John, so I''d consider this point addressed.

>   3.  What do you do about virtual resources that are realized in the
>       parent class?  You can''t unrealize them and if you override
their
> properties, you may well conflict with other modules or classes which
> use them.  #

Interesting point, I don''t think I''ve yet used virtual
resources to the
extent you imply.
It presents a problem that would have to be addressed using more
syntactical convolution, not dissimilar from point (2).

>   4.  What do you do about any other classes that are included in the
>       parent class?  Are you going to include "::disabled"
versions of
>       those in the ldap::client::disabled class?  What if those classes
>       are included elsewhere?

If disabling the base class necessarily implies disabling the included
class, this is the correct thing to do.
In most cases though, where ldap::client includes things it needs, but
which otherwise are not unique to ldap clients, forbidding their
presence in this way is a semantical error and should be avoided.
Deciding this is (as always) up to the designer.

And yes, it is another layer of complication for this approach.

>>  but the variable approach has severe limitations of its own.
>>
>> 1. Where is the variable set? You have probably no problem when using
>> external node definition, but not everybody does.
>
> You can do it in an internal node definition too.  Is there a law
> against node definitions in your manifests?  But let''s assume
somebody
> doesnt'' want to or cannot use nodes.  That isn''t a
problem; I''ve
> designed Puppet configurations that used no nodes at all, or a default
> node which simply included a class based on the node''s hostname.
Still
> works - just declare the variable at the right level.

Exactly. In a variable driven environment, where there are places
(nodes, external definitions, a central class with a node''s manifest
root) that fit the definition of "the right level", this is the most
sane approach. Using other design paradigms, however, there may not be a
"right level" to do that.
There is a reason for variable scoping biting many.

>> You cannot define the
>> variable in an arbitrary part of your manifest, because you can be
>> afflicted by both ordering and scoping issues.
>
> I wouldn''t try to declare it in an arbitrary location.  I would
declare
> it in an appropriate location.

Consider this manifest structure (that I in *no way* endorse):

node base_node {
  include default_stuff
}

node ldap_server1 inherits base_node {
  ...
}

class default_stuff {
  include ldap::client
}

The inclusion of ldap::client is in the scope of base_node, and thus
likely outside the area where normal nodes are allowed to change its
environment. You could conceive horrors like

class default_stuff {
  $exclude_ldap_client = ""
  if $exclude_ldap_client != "true" {
    include ldap::client
  }
}

class ldap::server {
  $default_stuff::exclude_ldap_client += "true"
}

Which is arguably distasteful.

>>
>> 2. The concept of dynamic scoping is going away in future versions (at
>> least that is what seems to be a community consensus). Again, if you
can
>> globally assign the variable value to your node, this is not a problem.
>
> It''s not that I''m in love with the way Puppet uses
variables - I
> certainly amd not - my objection is to the concept of trying to negate a
> class by individually trying to break or undo all it''s effects.
That
> latter option is a horrible mess.  Unless the community also proposes to
> do away with "if", "case" and selectors, there is
always going to be a
> way of saying "if <condition> { include ldap::client }" and
that is
> always going to be a better solution for the problem *as described by
> the original poster*, which is what I was answering.

I guess our notions of the original problem differ somewhat.
Seeing as I admit to many of your concerns being quite true, your
approach may be more fitting in the general case, that being:
a) the manifest has central locations (like elaborate node definitions)
where setting "node global" variables is not a problem and
b) the class that needs unincluding is rather complex

If both aren''t given (maybe an edge case), I''d still lean
towards the
subclassing approach.

>> But if you want to for every node that includes class B to
automatically
>> not include class A, you''re out of luck (this is true in many
cases
>> today already, because of the scoping issues).
>
> Well, you''ve finally given an example that raises a genuine
problem, but
> the first thing to say is that it''s not the problem described by
the
> poster.

It is. Whoever includes ldap::server is not to include ldap::client.
(Well yes, I sort of invented that first part, but this is what I''d
consider a clean structure in puppet terms.)

> Secondly, the problem you describe can be solved by some
> variant of
>   if $variable {
> 	include A
>   } else {
> 	include B
>   }

You''re kidding, right? Putting this in pseudo-logical terms, what I
asked for was

B => !A

and you''re proposing

!A => B and !B => A

So everytime I want something to not be an ldap client, it must become
an ldap server?

> Even if we outlaw simple variables, there are other ways to to signal
> state; parameterized classes, defines, creative abuse of virtual
> defines, all of them are better than the idea of negating a class
> resource by resource.

Very good point. My arsenal is very limited wrt. to the approaches
mentioned above. For most given problems, I can imagine that one (or a
combination) of those listed above fits much better than the plain
subclassing approach by itself.

>> Variables are about as far as you can get from a panacea in the puppet
>> DSL. Granted, class inheritance can be clumsy, awkward and
>> unmaintainable in many cases.
>
> Class inheritance is very elegant when used for what it is good for.  It
> is bad for the purposes of the original poster.

...based on your assumptions about the OP''s manifest.

>> But all that and more applies to variables
>> as well, and it seems to me that variables are going to become a lot
>> less useful for such purposes in the future.
>
> Wonderful.  As I said, I''m not in love with Puppet''s
variables.  I''ve
> worked with declaritive languages before - done major projects in XSLT
> and XSLT - and I''ll be delighted to see more structured, less
messy ways
> of working.  I''d be even more delighted to see some of what are
> currently functions become genuine keywords.  But if you''re
telling me
> that the community is not just proposing to remove dynamic variable
> scope but also to remove the ability to say "if <condition> {
include
> class }", not provide any alternatives and force people to have to
> clumsily override every resource in a class which they never wanted to
> include in the first place, then I''m afraid that the community is
an
> ass.  Hopefully, that''s not what you''re telling me.

I don''t believe it is :-)

The idea is that class parameters, once they work flawlessly, can
obsolete dynamic variable scoping. The latter can and should be done
away with because of conceptional problems. The other language features
you''re referring to are not subject to this development.

In your initial response, you proposed wrapping the included class in a
define, but warned against it. I''m not sure how a "2.7"
approach should
go about the problem, but I can imagine it might go kind of like

class ldap(client=true) {
  if $client { include ldap::client }
}

class defaults {
  class { "ldap": }
}

class defaults_no_ldap_client inherits defaults {
  Class["ldap"] { client => false }
}

class ldap::server {
  include defaults_no_ldap_client
}

No littering your manifest with variables that float around wildly, and
no wild overriding of multiple resources.
Of course, this radically disagrees with John''s notion (and is actually
a cleaner implementation of Bruce''s proposal).
But after all, my point wasn''t what John was thinking of, and my
primary
problem is with flinging variables across class boundaries.

Cheers,
Felix