early clean exit from module?

4,342 views
Skip to first unread message

Philip Brown

unread,
Dec 17, 2012, 3:02:53 PM12/17/12
to puppet...@googlegroups.com
Hi folks,
I've poked around the language reference, and havent found anything on this so far. I'd like to be able to force a clean early exit from a module. WITHOUT encasing the whole body of the module in an if statement.

What I'm looking for:

class somemodule {
  #no this isnt going to be a global variable I'm just using that as an example,
  if $dont_load_module {
      return happy
  }
  // blah blah normal module goes on here
}

What I'd like to AVOID:

class somemodule {
  if $dont_load_module != true {
    // body of module
    // potentially very long
    // goes
    // here
  }
}


I know there are a few different ways to skin this cat that come at the issue from a different angle; however, I'd appreciate a direct puppet language solution, rather than "go restructure the way you load modules via ENC/blahblah", if possible.

Chad Huneycutt

unread,
Dec 17, 2012, 6:00:07 PM12/17/12
to puppet...@googlegroups.com
I do not think what you want exists.  It makes sense, as a class is not a procedural list of commands to execute, but a collection of resources, so a way to "exit early" does not really exist.   If you just want to avoid the extra indentation and potentially misplaced closing bracket, how about this pattern:

class somemodule {
   if $load_module = true {
      include somemodule::load
    }
  }

  class somemodule::load {
    // potentially very long module
  }


- Chad



--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/puppet-users/-/F0HcnaYmK8YJ.
To post to this group, send email to puppet...@googlegroups.com.
To unsubscribe from this group, send email to puppet-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.



--
Chad M. Huneycutt

Jakov Sosic

unread,
Dec 17, 2012, 6:18:26 PM12/17/12
to puppet...@googlegroups.com
On 12/17/2012 09:02 PM, Philip Brown wrote:
> Hi folks,
> I've poked around the language reference, and havent found anything on
> this so far. I'd like to be able to force a clean early exit from a
> module. WITHOUT encasing the whole body of the module in an if statement.
>

Puppet is not procedural but declarative language, so you can only
declare states. Don't try to control the flow, because ordering of
execution is calculated by dependencies between resources and not by
location of resource definition in the manifest.


--
Jakov Sosic
www.srce.unizg.hr

Philip Brown

unread,
Dec 17, 2012, 7:21:25 PM12/17/12
to puppet...@googlegroups.com


On Monday, December 17, 2012 3:18:26 PM UTC-8, Jakov Sosic wrote:


Puppet is not procedural but declarative language, so you can only
declare states.

Except that is not strictly true.
There is an early-exit "fail" directive that can be used conditionally.
So why not a conditional early exit from a module, that allows other modules to keep going?
Perhaps it would help if I rephrased "exit" as some other more state-friendly language, but nothing is coming to mind at the moment.



Peter Brown

unread,
Dec 17, 2012, 10:07:33 PM12/17/12
to puppet-users
The most important question is what are you trying to achieve?

From what I can tell from your post you are putting the decision on whether to include the resources in a module in the wrong place.
If the module shouldn't be included in a node don't include it in the node declaration.
Deciding that in the module is the wrong place especially if you are excluding the all the resources in a module for one (or even a few) cases.

You need to switch your thinking when writing puppet modules.
Trying to write procedural code in a declarative environment will cause all kinds of headaches. (I know because It took me a bit to understand how it worked)
Admittedly you can use mildly procedural constructs in your modules and classes and such but it's not quite the same because the resources within those constructs won't be "executed" in the order you write them either.
The "Order" puppet applies the resources to a node is dependent on the relationships between the resources (require, before, subscribe and the like).

I hope that sheds some light on your issue.
If you want to provide some more details on what you are trying to achieve I am happy to help you work out the best way to realise it.

Pete.

Jakov Sosic

unread,
Dec 18, 2012, 9:07:24 AM12/18/12
to puppet...@googlegroups.com
On 12/18/2012 01:21 AM, Philip Brown wrote:

> Except that is not strictly true.
> There is an early-exit "fail" directive that can be used conditionally.
> So why not a conditional early exit from a module, that allows other
> modules to keep going?

Because module is not a function, and resources in the module are not
commands. And Puppet is not procedural language as I've noted already.

So, your only way to achieve what you are trying is to encase all
resources in the conditional, and since you don't want to do that, then
either you modify your node classifier not to include that class at the
first place or change the approach to the initial problem you are trying
to solve.


> Perhaps it would help if I rephrased "exit" as some other more
> state-friendly language, but nothing is coming to mind at the moment.

You're trying to solve your problem at the wrong level. You should do it
on the ENC and not class level.

jcbollinger

unread,
Dec 18, 2012, 10:41:44 AM12/18/12
to puppet...@googlegroups.com


No, it wouldn't.  You are missing the point of the 'fail' function, which is to declare that it is impossible to compile a correct catalog for the target node.  Early parsing abortion is merely a side effect.

The bottom line is that the Puppet language does not have what you are asking for, given the tight constraints you have expressed on the solutions you are willing to accept.  Frankly, it's not well aligned with the Puppet paradigm.  You will have much more success and much less frustration (over time) if you learn to work within Puppet's natural structure and style than if you insist on fighting against it.

The other respondents are right: if you don't want a class assigned to a node then you should avoid assigning it to the node.  It's pretty silly to assign it but make that conditionally meaningless.  You don't necessarily need to pull the logic all the way up to an ENC, but you should pull it up at least one level.


John

Philip Brown

unread,
Dec 18, 2012, 11:29:27 AM12/18/12
to puppet...@googlegroups.com


On Tuesday, December 18, 2012 7:41:44 AM UTC-8, jcbollinger wrote:
The other respondents are right: if you don't want a class assigned to a node then you should avoid assigning it to the node.  It's pretty silly to assign it but make that conditionally meaningless.  You don't necessarily need to pull the logic all the way up to an ENC, but you should pull it up at least one level.




The problem is that I have yet to see a solution that is both flexible, and local, for defining which modules get included for a node.

The only thing I've seen so far, are references to things like "Dashboard". Which requires installing a full-on separately running database, blah blah blah.

I was originally hoping that hiera would be able to let me do this, but after learning more about it, it no longer seems to be a good match.

Unless, I suppose, I wrap every individual module in a general node definition with something like

if hiera("load_ntp"){
   include ntp
}
if heira("load_snmp"){
   include snmp
}
if hiera("load_nfsserver"){
   include nfsserver
}

And even that has its limits. Heira config matching is fairly dumb. As far as I'm aware, while the top level yaml config can do things like
${hostname}.yaml

It cant do things like

if $hostname =~ /.*-db[0-9/     load dbconfigs.yaml


Ellison Marks

unread,
Dec 18, 2012, 12:33:13 PM12/18/12
to puppet...@googlegroups.com
You're looking for the hiera_include() function, I believe. It's undocumented in the official docs, which is a shame, but I'll try to describe it.

Your node definition would look something like this:

node default {
  hiera_include('classes')
}

This instructs every node to look up the classes variable in hiera. It doesn't need to be named classes, but I like that name. This variable should evaluate to a list of strings. Each of these strings will then be treated as a class name and puppet will attempt to 'include' it. You make your hierarchy so that each node gets an appropriate list.

Philip Brown

unread,
Dec 18, 2012, 1:20:21 PM12/18/12
to puppet...@googlegroups.com


On Tuesday, December 18, 2012 9:33:13 AM UTC-8, Ellison Marks wrote:
You're looking for the hiera_include() function, I believe. It's undocumented in the official docs, which is a shame, but I'll try to describe it.

Your node definition would look something like this:

node default {
  hiera_include('classes')
}

This instructs every node to look up the classes variable in hiera. It doesn't need to be named classes, but I like that name. This variable should evaluate to a list of strings. Each of these strings will then be treated as a class name and puppet will attempt to 'include' it. You make your hierarchy so that each node gets an appropriate list.




Hmm. Thanks for the explanation. It sounds *potentially* useful. However, still one problem:
I dont think it's additive.

In other words, you cant have a truly modular flow in hiera, where you have
the following pseudocode

$(allhosts )  => gets 'classes' list from common.yaml

$(dbmachines)  => gets additional classes from dbhosts.yaml

$(webmachines) => gets additional classes from webhosts.yaml


If you want a host that is both a db machine, AND a webserver, you need to create a new hiera file,
dbandwebhost.yaml
and then make sure that all machines in that context, get pointed to that specific file, rather than getting to merge things.


The only method I see that uses the "pure" hiera_include method you show above, and still allows for mix-and-max class includes, would be to handcraft individual

FQDN.yaml

files for each and every host.  ?




Jakov Sosic

unread,
Dec 18, 2012, 1:23:54 PM12/18/12
to puppet...@googlegroups.com
On 12/18/2012 07:20 PM, Philip Brown wrote:

> Hmm. Thanks for the explanation. It sounds *potentially* useful.
> However, still one problem:
> I dont think it's additive.
>
> In other words, you cant have a truly modular flow in hiera, where you have
> the following pseudocode
>
> $(allhosts ) => gets 'classes' list from common.yaml
>
> $(dbmachines) => gets additional classes from dbhosts.yaml
>
> $(webmachines) => gets additional classes from webhosts.yaml
>
>
> If you want a host that is both a db machine, AND a webserver, you need
> to create a new hiera file,
> dbandwebhost.yaml
> and then make sure that all machines in that context, get pointed to
> that specific file, rather than getting to merge things.
>
>
> The only method I see that uses the "pure" hiera_include method you show
> above, and still allows for mix-and-max class includes, would be to
> handcraft individual
>
> FQDN.yaml
>
> files for each and every host. ?

You can write your own ENC script, which gathers all the classes from
different files and makes join instead of returning first result.

http://docs.puppetlabs.com/guides/external_nodes.html

Ellison Marks

unread,
Dec 18, 2012, 1:28:35 PM12/18/12
to puppet...@googlegroups.com
No, it's additive. It will get all the class names from all hierarchy levels that a host maps to. It uses almost the same code as hiera_array(), (also undocumented XP, it essentially builds an array out of all the elements in the specified variable at all levels of the hierarchy matching the host), then just calls include on the elements in the resulting list.

R.I.Pienaar

unread,
Dec 18, 2012, 1:31:55 PM12/18/12
to puppet...@googlegroups.com


----- Original Message -----
> From: "Ellison Marks" <gty...@gmail.com>
> To: puppet...@googlegroups.com
> Sent: Tuesday, December 18, 2012 6:28:35 PM
> Subject: Re: [Puppet Users] early clean exit from module?
>
> No, it's additive. It will get all the class names from all hierarchy
> levels that a host maps to. It uses almost the same code as
> hiera_array(), (also undocumented XP, it essentially builds an array
> out of all the elements in the specified variable at all levels of
> the hierarchy matching the host), then just calls include on the
> elements in the resulting list.

I think it's not documented cos in recent versions of puppet its not needed
you can do include(hiera_array("classes")) or whatever, older puppet - ie
when hiera was first written - had a bug with array input to the include
function.
> --
> You received this message because you are subscribed to the Google
> Groups "Puppet Users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/puppet-users/-/Vx2_K8OIbREJ .
> To post to this group, send email to puppet...@googlegroups.com.
> To unsubscribe from this group, send email to
> puppet-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/puppet-users?hl=en.
>
> No, it's additive. It will get all the class names from all hierarchy
> levels that a host maps to. It uses almost the same code as
> hiera_array(),
> (also undocumented XP, it essentially builds an array out of all the
> elements in the specified variable at all levels of the hierarchy
> matching
> the host), then just calls include on the elements in the resulting
> list.
>
> On Tuesday, December 18, 2012 10:20:21 AM UTC-8, Philip Brown wrote:
> >
> >
> >
> > On Tuesday, December 18, 2012 9:33:13 AM UTC-8, Ellison Marks
> > wrote:
> >>
> >> You're looking for the hiera_include() function, I believe. It's
> >> undocumented in the official docs, which is a shame, but I'll try
> >> to
> >> describe it.
> >>
> >> Your node definition would look something like this:
> >>
> >> node default {
> >> hiera_include('classes')
> >> }
> >>
> >> This instructs every node to look up the classes variable in
> >> hiera. It
> >> doesn't *need* to be named classes, but I like that name. This
> --
> You received this message because you are subscribed to the Google
> Groups "Puppet Users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/puppet-users/-/Vx2_K8OIbREJ.

Jakov Sosic

unread,
Dec 18, 2012, 1:41:50 PM12/18/12
to puppet...@googlegroups.com
On 12/18/2012 07:28 PM, Ellison Marks wrote:
> No, it's additive. It will get all the class names from all hierarchy
> levels that a host maps to. It uses almost the same code as
> hiera_array(), (also undocumented XP, it essentially builds an array out
> of all the elements in the specified variable at all levels of the
> hierarchy matching the host), then just calls include on the elements in
> the resulting list.


Woow cool I didn't know about it!

Ellison Marks

unread,
Dec 18, 2012, 1:43:28 PM12/18/12
to puppet...@googlegroups.com
I like the more compact syntax. ^_^
I suppose the other way would be more consistent though.

As far as documentation goes though, hiera_array() is just as undocumented as hiera_include(), which is just as undocumented as the other hiera* functions.
http://docs.puppetlabs.com/references/latest/function.html#hiera

Philip Brown

unread,
Dec 18, 2012, 1:43:44 PM12/18/12
to puppet...@googlegroups.com


On Tuesday, December 18, 2012 10:28:35 AM UTC-8, Ellison Marks wrote:
No, it's additive. It will get all the class names from all hierarchy levels that a host maps to.

Waaait a minute. Now I'm really confused about hiera :(

I thought hiera was usually replacing, not additive.

Or is it that single-value hiera lookups, are replacing, but array lookups are additive?

holy inconsistencies, batman!
We almost implemented something wrong then. We were planning on using hiera to pull a default array of NTP servers, but then override the default list, if at a sattelite office.  But you're telling me that since it's an array, it will add, not override? !

Ellison Marks

unread,
Dec 18, 2012, 1:47:26 PM12/18/12
to puppet...@googlegroups.com
Yeah, hiera() will bail the moment it finds an answer. I *think* that one correct answer can be an array or hash, but test it first. Or ask R.I.P.

hiera_array() and hiera_hash() are both additive, building out the array or hash from values at every level of the hierarchy.

jcbollinger

unread,
Dec 18, 2012, 4:50:19 PM12/18/12
to puppet...@googlegroups.com


No.  The hierra_array() and hiera_hash() functions are always additive.  The the plain hiera() function is never additive.

That is completely unrelated to the type of data being retrieved.  The plain hiera() function will return an array or hash if that is the type of the item matching the specified key.

By the same token, the elements of the array returned by hiera_array() will be of whatever type appears in your data file for the given key at the corresponding hierarchy level.  In particular, if you have lists of classes at each level, then hiera_array() will retrieve it all as an array of arrays, which you would need to flatten.  PuppetLabs' "stdlib" add-on module contains a suitable flatten() function, or you could write your own.


John

Ellison Marks

unread,
Dec 18, 2012, 5:26:54 PM12/18/12
to puppet...@googlegroups.com
Yeah, I thought it was something like that, just hadn't checked myself.

As to flattening however, I don't think that's necessary on the user's part. Include calls flatten on any array it is passed automatically.

Ellison Marks

unread,
Dec 18, 2012, 5:35:14 PM12/18/12
to puppet...@googlegroups.com
Unless you meant using hiera_array() for general use, in which case yes, you probably need to flatten it.

jcbollinger

unread,
Dec 19, 2012, 8:52:59 AM12/19/12
to puppet...@googlegroups.com


On Tuesday, December 18, 2012 4:26:54 PM UTC-6, Ellison Marks wrote:
Yeah, I thought it was something like that, just hadn't checked myself.

As to flattening however, I don't think that's necessary on the user's part. Include calls flatten on any array it is passed automatically.


Why, so it does!  Thanks, I learned something new today.


John

Reply all
Reply to author
Forward
0 new messages