These emails are getting really long. Let's try this: I'll restate
what I think you're recommending, you correct any as necessary, and
we'll see if that can make the discussion a bit more succinct. I'll
then heavily trim your email and respond to the points that I think
are particularly comment-worthy.
My main conclusion here, though, is that you're talking about a
language that bears not a lot of resemblance to Puppet, in the end,
and if this is something you want, then I recommend starting by
essentially forking Puppet's parser and working from there. Either
that, or trying to turn the current DSL.rb into the language you want,
although obviously it's pure ruby.
I'm not saying you shouldn't do these things, just saying that you're
talking about drastic changes to the core of the language for what I
see as questionable benefit.
On to specifics:
Removal of 'class' and 'node':
Node, class, and define are all merged into a single syntactical
element. This element is effectively equivalent to the existing
'define' element, in that it accepts parameters and is unique only for
a given name. That is, you can always create a new instance of this
element, as long as you give it a different name. When used with no
name, the name is automatically set to the name of the element itself.
Node becomes a builtin resource type:
I'm a bit unclear on this. You mention this as a way to continue
providing an automatic entry point for compiling, and you make
reference to turning the facter variables into node attributes. I
don't understand this well enough to know how it'd even work.
'include' goes away:
One no longer includes classes, and instead classes are specified
using the normal resource-specification syntax. I'm pretty unclear on
how you'd do this for the typical what-we-currently-call-classes, with
no unique name and no parameters. Would it be:
webserver { : }
Or maybe:
webserver { webserver: }
This is actually how Puppet started, and I got rid of it because of
how obviously unobvious they are.
You also say something about relationships being used instead of
'include', so I assume you mean there that specifying a relationship
to a class would automatically evaluate that class.
Introduction of the keyword 'self':
I actually don't know what you're doing here, but you're using it in
all of your examples, so I figured I'd point it out.
Templates become resources:
Move templates from a function to a resource to avoid implicit
consumption of variables. I assume this would involve modifying
properties to know how to retrieve a template's content when
necessary. I.e., you'd need to do something like:
template { "whatever": foo => bar }
file { "/my/file": content => Template[whatever] }
You'd need to do this for every property, otherwise you'd lose the
current ability of using templates for any rvalue.
Okay, now for specific (hopefully short) commentary:
On Nov 4, 2008, at 10:09 PM, Florian Grandel wrote:
>
> #
> # We use inheritance here but that's not necessary, we could
> # also use a "mix-in" bundle that is simply included or
> # "required" by other bundles.
> bundle base-node inherits node {
[...]
>
> }
>
> # Define a resource-bundle that represents a
> # web-server node
> bundle super-duper-webserver-node inherits base-node {
[...]
>
> }
>
> # Now let's instantiate our nodes
> super-duper-webserver-node {
> "myhost1": ;
> "myhost2": ;
> ...
> }
Does this mean that we could have multiple subinstances or whatever
you call them for a given node? That is, we could have webserver
{ mynode: } and dbserver { mynode: }?
Also, where are facts here? You've mentioned making them no longer
global constants, but I don't know where they went.
> You see in my example that "node" is no longer something special. It
> probably is a built-in "resource-bundle" like "package", "file" or
> whatever that provides read-only instance attributes rather than using
> global facter variables that miraculously appear from "nowhere". (This
> shows the point about read-only attributes that I made in my
> response to
> the other thread.) Internally facter may provide such attributes, but
> does the user have to be concerned with such implementation detail???
I don't know if you're giving too much or too little credit to the
Puppet user, but trust me, where those facts come from and how they
become available in the language is not just an implementation
detail. It's a critical part of the framework, and most users end up
needing to understand it very well, and I don't see anything in your
recommendation that's changed that.
>
> The only difference is that now "classes", "definitions", "types" and
> "nodes" can all be inherited, parameterized, expanded and whatever,
> all
> the same without any "artificial" distinctions that have to be
> learned.
> I think that this one syntax for all is really much easier to
> understand
> and learn for everybody. You hide away a lot of complexity.
This seems to be the crux of your argument -- that removing
syntactical variety is simpler and hides complexity -- but I'm not
convinced. Puppet's simplicity largely comes from its lack of options
or complexity -- would you say LISP is simpler or more complex than
Puppet? One could argue either way, but I tend toward thinking its
simplicity makes it potentially more complicated to use. Certainly it
raises the bar for the programmer who would be involved.
>
> I personally don't believe that a sysadmin "intuitively" thinks about
> classes and definitions. To the contrary: The fact that you have to
> dedicate a own chapter in the documentation and answer many IRC
> questions about when to use classes and when to use definitions
> seems to
> show that the distinction is not really intuitive. I personally think
> it's more intuitive to do away with it. Maybe you should start some
> kind
> of poll for first time users?
Having spent the last three months attempting to teach my infant
daughters how to nurse, I'm not a big believer in intuitiveness.
Declarative languages aren't intuitive, modeling resources instead of
files isn't intuitive, asynchronous administration (i.e., commit and
wait) isn't intuitive; yet people have learned all of these and
wouldn't give them up, in most cases.
>
> I think its obvious however that the "bundle-syntax" is as readable as
> the current language but at the same time easier to refactor and
> expand.
I think you are missing a lot of corner cases (or maybe not-so-corner)
that make it less so. In particular, the common case of a "class"
with no name or parameter is particularly jarring syntactically:
webserver { : }
For the record, if you look at the earliest versions of Puppet, the
'include' function was just a shortcut for exactly this syntax.
This gets silly on relationships, too; do I do:
require => Webserver[]
or:
require => Webserver[webserver]
or what?
[...]
> Give me some examples of use-cases where you think you cannot do
> without
> multiple includes or where the "resource-bundle" syntax would be
> counter-intuitive and I'll see whether I have an error in my logic or
> whether I can formulate your use-cases in a satisfactory way in
> "bundle
> syntax".
You haven't made it explicit, but I assume you mean that specifying a
relationship to a class would result in automatic evaluation. That
is, I could say 'require => Webserver[foo]' and it would automatically
evaluate that class? Or would I need to evaluate it elsewhere in
order to specify a relationship?
>
>> nodes have
>> automatic entry points (i.e., the parser accepts a request from a
>> host
>> and uses the host's name to look up a node instance). Neither
>> classes
>> nor definitions have automatic entry.
>
> The built-in resource-bundle "node" continues to be an automatic entry
> point for puppetd. But IMO that's an unnecessary implementation detail
> that can be hidden away from the user.
Again, I don't think this is an implementation detail -- it's a
fundamental aspect of how information enters Puppet, and it's
something users really need to undestand.
>
>> To me, the term 'class' is a convenient way to describe the
>> configuration associated with a given intent. "Web server" means one
>> list of resources, "dns client" means another, etc.
>
> There are many places where the difference between an "intent" and a
> "resource" is blurred:
> - Is a webserver a resource or an intent?
> - Is a subversion server a resource or an intent?
> - If I have to add a second database instance to my database server,
> is
> my "database-server class" now suddenly mutating from an intent
> towards
> a resource?
It seems pretty simple: you must be a database server in order to run
database instances. Classes define a service that you provide, and
resources are some aspect of that service.
[...]
> Now comes my point about "clusters" or "class of nodes". In the
> generalized "bundle-syntax" you can say:
>
> bundle full-system-cluster($needs_firewall = true) {
> if $self::needs_firewall {
> firewall-and-load-balancer-node { "${self::name}-fw": }
> } else {
> load-balancer-node { "${self::name}-lb": }
> }
>
> webserver-node {
> "${self::name}-web1": ;
> "${self::name}-web2": ;
> }
>
> application-server-node {
> "${self::name}-app1":
> "${self::name}-app2":
> }
>
> db-server-node { "${self::name}-db": }
> }
>
> And now you can instantiate whole clusters with the same
> infrastructure
> of containing nodes:
>
> full-system-cluster {
> "production-cluster": ;
> "staging-cluster": ;
> "development-cluster": needs_firewall => false;
> }
>
> You'd effectively configure 18 hosts (three groups of six) in just
> three
> lines. Isn't that super readable and intuitive, and clearly showing
> intent? Try to express the same thing with disparate node declarations
> (in a .pp file or as external nodes) and you'll have duplicated code
> and/or data en masse.
>
> That's what I meant with "node instances". Sure, puppetd will still
> "enter" at the node level. But why not going down /and/ up in the
> hierarchy to find out about how to configure our specific node? You
> could still derive one single catalog per node.
I don't think I understand this at all. Given a node name, how does
it know that it's a part of the system cluster? If it's part of the
cluster, how does it know which services from the cluster it provides?
AFAICT, I could do this exact thing with Puppet's classes right now.
>> the lack of meaningful distinction [between node and class] is one
>> thing that led me to want to push nodes outside of Puppet.
>
> You can continue to do so. I believe that the more generic "bundle
> syntax" will help you to simplify and extend your "external definition
> provider" implementations. You could provide external parameters not
> only on type and node level but also in between or above wherever you
> like in the bundle hierarchy. All instantiation can be done
> externally.
> You could keep only bundle definitions in your *.pp files and get all
> the rest from an external source.
This seems to be pretty far out -- the current interface to external
nodes is very simple, and it sounds like you're talking about a pretty
dramatic enhancement to this external interface. I'd say leave that
discussion as a separate problem entirely.
[...trying to hurry to actually finish this email...]
>> I don't like the idea of declaring class membership via the same
>> syntax for specifying resources, partially because I think most
>> people
>> will find that the attributes that are consumed by a class are often
>> specified in a different location than the code that specifies class
>> membership.
>
> Can you give an example? I don't understand that. IMO separating
> parameter and class instances is a design error. This is exactly what
> makes code error prone and difficult to maintain as it increases the
> the
> amount of code you have to consider when refactoring classes.
I can't come up with much in terms of specific examples right now, but
I'm kinda short on time. We can effectively ignore this discussion,
except that I'm already planning on building a web interface that
would make it easy to configure nodes and their classes; this
interface would want to query a class to see what parameters are
required and then require that those params be filled in on the
external node tool. This is a clear separation, and you'd clearly
want it -- I don't want to have to explicitly import those params or
something for the node.
>
> If you have to nest bundles at several levels then I consider it good
> practice to re-declare required parameters at every level:
>
> bundle shorewall::rule($ip, $port, ...) {
> ...
> file{...template(...using $self::ip and $self::port...)...}
> ...
> }
As an aside, where is this '$self' stuff from? You haven't explicitly
mentioned it, but it seems to be in all of your examples.
[...]
> And one final idea concerning templates:
>
> If you made templates a built-in "resource bundle" rather than a
> function it would be very easy to explicitly declare parameters.
>
> You could simply say:
>
> template { "/my/template.erb": var1 => ..., var2 => ... }
>
> All variables not explicitly declared will simply not be present in
> the
> template.
>
> This would remove the last occurrence of undeclared dependencies. I
> don't see why you couldn't use ruby's template engine internally
> anyway.
As mentioned at the top, this would require teaching the Parameter
class how to understand template instances. I guess I'm not entirely
opposed to this, but it seems a bit unnecessary, at the same time.
--
Censorship, like charity, should begin at home; but, unlike charity, it
should end there. --Clare Booth Luce
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---