variable scoping?

151 views
Skip to first unread message

Zachary Stern

unread,
Aug 11, 2012, 8:45:56 PM8/11/12
to puppet...@googlegroups.com
I'm having a really hard time grasping how variables are scoped in
puppet (not really much of a programmer).

I've got a manifest that looks like this:
###
class puppet::config {
include puppet::params
file { '/etc/puppet/puppet.conf':
ensure => present,
content => template('puppet/puppet.conf.erb'),
owner => 'root',
group => 'admins',
require => Class['puppet::install'],
notify => Class['puppet::service'],
}
}
###


I've then got a manifest like this, which has a class being included above:
###
class puppet::params {
$puppetserver = 'command.enterawesome.com'
}
###

The template being used in the first class includes the variable
$puppetserver, but somehow, the include statement isn't enough for the
variable to be defined within the scope of the manifest/template
above.
What gives?
It works just fine if I include a statement like this in the erb file:
<%= scope.lookupvar('puppet::params::puppetserver') %>

But I'd really like to understand scoping better. What is it I need to
do to just refer to the variable by name? Why isn't the include
statement enough? Isn't in including the puppet::params class inside
the puppet::config class, and therefore having the variable defined in
that context? Apparently not. But I don't understand why. I wan't to
end up at a point where the variable is in the proper scope, such that
I can just have a statement like <%= puppetserver %> inside of the
template I'm using.

Thanks in advance!

Eric Shamow

unread,
Aug 11, 2012, 10:36:48 PM8/11/12
to puppet...@googlegroups.com
The best reference to explain how variable scoping works in Puppet is this one -

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

Scoping has changed with 2.7, so you may find some confusing references online that follow the pre-2.7 rules. In general the 2.7 changes are designed to introduce some sanity to variable scopes and eliminate dynamic scoping, which causes all kinds of pain.

The easiest way to think about scoping in general is that a scope is defined by its container. If I put something physical in a box, I have access to it the moment I open the box, and other objects inside the box can interact with it. If, however, I now put that box inside a second box, the objects in the second box cannot interact directly with the objects in the first - the first object is protected by its container.

Scoping mostly works the same way. The right way to get at the variable is to always explicitly scope, as you began to get at below with your scope.lookupvar example. As that can be a bit of a pain to repeat, you can always copy the value into a local variable:

class puppet::config {
include puppet::params
$puppetserver = puppet::params::puppetserver

}

-Eric

--

Eric Shamow
Professional Services
http://puppetlabs.com/
©631.871.6441

Join us for PuppetConf 2012 at the Mission Bay Convention Center in San Francisco, California on September 27th and 28th --> http://bit.ly/pcsig12


On Saturday, August 11, 2012 at 8:45 PM, Zachary Stern wrote:

> I'm having a really hard time grasping how variables are scoped in
> puppet (not really much of a programmer).
>
> I've got a manifest that looks like this:
> ###
> class puppet::config {
> include puppet::params
> file { '/etc/puppet/puppet.conf':
> ensure => present,
> content => template('puppet/puppet.conf.erb'),
> owner => 'root',
> group => 'admins',
> require => Class['puppet::install'],
> notify => Class['puppet::service'],
> }
> }
> ###
>
>
> I've then got a manifest like this, which has a class being included above:
> ###
> class puppet::params {
> $puppetserver = 'command.enterawesome.com (http://command.enterawesome.com)'
> }
> ###
>
> The template being used in the first class includes the variable
> $puppetserver, but somehow, the include statement isn't enough for the
> variable to be defined within the scope of the manifest/template
> above.
> What gives?
> It works just fine if I include a statement like this in the erb file:
> <%= scope.lookupvar('puppet::params::puppetserver') %>
>
> But I'd really like to understand scoping better. What is it I need to
> do to just refer to the variable by name? Why isn't the include
> statement enough? Isn't in including the puppet::params class inside
> the puppet::config class, and therefore having the variable defined in
> that context? Apparently not. But I don't understand why. I wan't to
> end up at a point where the variable is in the proper scope, such that
> I can just have a statement like <%= puppetserver %> inside of the
> template I'm using.
>
> Thanks in advance!
>
> --
> You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> To post to this group, send email to puppet...@googlegroups.com (mailto:puppet...@googlegroups.com).
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com (mailto:puppet-users...@googlegroups.com).
> For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.



Eric Shamow

unread,
Aug 11, 2012, 10:37:48 PM8/11/12
to puppet...@googlegroups.com
Sorry, that should be

$puppetserver = $puppet::params::puppetserver

-Eric

--

Eric Shamow
Professional Services
http://puppetlabs.com/
©631.871.6441

Join us for PuppetConf 2012 at the Mission Bay Convention Center in San Francisco, California on September 27th and 28th --> http://bit.ly/pcsig12


Zachary Alex Stern

unread,
Aug 11, 2012, 11:23:05 PM8/11/12
to puppet...@googlegroups.com
Sorry, that should be

$puppetserver = $puppet::params::puppetserver

-Eric  


Seems like at that point I may as well just do this inside the template: <%= scope.lookupvar('puppet::params::puppetserver') %> 

Zachary Alex Stern

unread,
Aug 12, 2012, 1:24:13 AM8/12/12
to puppet...@googlegroups.com
Also, fwiw, I've read that document on scoping beginning to end several times. Doesn't mean much to me I'm afraid - pretty new to all this.
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com (mailto:puppet-users+unsub...@googlegroups.com).

Douglas Garstang

unread,
Aug 12, 2012, 1:33:29 AM8/12/12
to puppet...@googlegroups.com
I feel your pain.
>> > (mailto:puppet-users...@googlegroups.com).
>> > For more options, visit this group at
>> > http://groups.google.com/group/puppet-users?hl=en.
>>
>>
>>
> --
> 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/-/IgWXSc13m04J.
>
> 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.



--
Regards,

Douglas Garstang
http://www.linkedin.com/in/garstang
Email: doug.g...@gmail.com
Cell: +1-805-340-5627

Eric Shamow

unread,
Aug 12, 2012, 1:38:21 AM8/12/12
to puppet...@googlegroups.com
Zachary,

Is there a particular section of the scoping doc I can help you understand?

-Eric

--

Eric Shamow
Professional Services
http://puppetlabs.com/
©631.871.6441

Join us for PuppetConf 2012 at the Mission Bay Convention Center in San Francisco, California on September 27th and 28th --> http://bit.ly/pcsig12


> > > $puppetserver = 'command.enterawesome.com (http://command.enterawesome.com) (http://command.enterawesome.com)'
> > > }
> > > ###
> > >
> > > The template being used in the first class includes the variable
> > > $puppetserver, but somehow, the include statement isn't enough for the
> > > variable to be defined within the scope of the manifest/template
> > > above.
> > > What gives?
> > > It works just fine if I include a statement like this in the erb file:
> > > <%= scope.lookupvar('puppet::params::puppetserver') %>
> > >
> > > But I'd really like to understand scoping better. What is it I need to
> > > do to just refer to the variable by name? Why isn't the include
> > > statement enough? Isn't in including the puppet::params class inside
> > > the puppet::config class, and therefore having the variable defined in
> > > that context? Apparently not. But I don't understand why. I wan't to
> > > end up at a point where the variable is in the proper scope, such that
> > > I can just have a statement like <%= puppetserver %> inside of the
> > > template I'm using.
> > >
> > > Thanks in advance!
> > >
> > > --
> > > You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> > > To post to this group, send email to puppet...@googlegroups.com (javascript:) (mailto:puppet...@googlegroups.com (javascript:)).
> > > To unsubscribe from this group, send email to puppet-users...@googlegroups.com (javascript:) (mailto:puppet-users...@googlegroups.com (javascript:)).
> > > For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.
> >
>
> --
> 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/-/IgWXSc13m04J.
> To post to this group, send email to puppet...@googlegroups.com (mailto:puppet...@googlegroups.com).
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com (mailto:puppet-users...@googlegroups.com).

Zachary Alex Stern

unread,
Aug 12, 2012, 2:35:53 AM8/12/12
to puppet...@googlegroups.com
So, your explanation makes sense to me - but that doesn't exactly explain to me why the "include" statement isn't enough.

E.g. when I'm "including the puppet::params class in the puppet::config class, what affect is it having at all, if not setting things like the variables included in the original puppet::params class?

How can I change variables on the fly effectively, if I can't just do the import and then have the variable $puppetserver equal the one from the imported class? I'm having a hard time grasping what import does, if not this.

On Saturday, August 11, 2012 10:36:48 PM UTC-4, Eric Shamow wrote:
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com (mailto:puppet-users+unsub...@googlegroups.com).

Eric Shamow

unread,
Aug 12, 2012, 8:43:53 AM8/12/12
to puppet...@googlegroups.com
First off, it's important to distinguish import from include. They "feel" like similar concepts but they're not - import goes and physically loads a file. Include says "ensure that this class is part of this host's resource graph."

It's important not to think of Puppet in procedural, top-down terms. We haven't just said, "give me all the stuff in this class, and now do these things." Rather we are building a model of a server, and manifest code simply informs pieces of that model. So by saying "include <class>" you are really saying, "Puppet, if you haven't already, make sure this class is part of the overall resource graph."

The better model for this example is, rather than a box inside of a box, you are looking at two sealed boxes sitting side by side. When you include the first group, it is added to the list of boxes used. Within the first box, when you then say "give me the second box," that second box is placed *alongside* the first. Thus in order to grab anything from it, you have to specify "I need the stuff from box 2."

Does that analogy help a bit more?

The reason for this is that, as your number of classes grows, so too will your variables. If they share a single global namespace, the likelihood of a global variable name being used more than once increases. This can lead to really unexpected changes and ambiguity in building the graph.

An example: suppose is this module you say $puppetmaster = "myserver.local" and in another module, you want to only run on a puppet master and so say $puppetmaster = true. When Puppet assembles the model of your system, which of the two is to be applied where? Dynamic scoping tried to guess at this. The idea here is to say, eliminate ambiguity - tell us exactly which one you want.

-Eric

--

Eric Shamow
Professional Services
http://puppetlabs.com/
©631.871.6441

Join us for PuppetConf 2012 at the Mission Bay Convention Center in San Francisco, California on September 27th and 28th --> http://bit.ly/pcsig12


> > > $puppetserver = 'command.enterawesome.com (http://command.enterawesome.com) (http://command.enterawesome.com)'
> > > }
> > > ###
> > >
> > > The template being used in the first class includes the variable
> > > $puppetserver, but somehow, the include statement isn't enough for the
> > > variable to be defined within the scope of the manifest/template
> > > above.
> > > What gives?
> > > It works just fine if I include a statement like this in the erb file:
> > > <%= scope.lookupvar('puppet::params::puppetserver') %>
> > >
> > > But I'd really like to understand scoping better. What is it I need to
> > > do to just refer to the variable by name? Why isn't the include
> > > statement enough? Isn't in including the puppet::params class inside
> > > the puppet::config class, and therefore having the variable defined in
> > > that context? Apparently not. But I don't understand why. I wan't to
> > > end up at a point where the variable is in the proper scope, such that
> > > I can just have a statement like <%= puppetserver %> inside of the
> > > template I'm using.
> > >
> > > Thanks in advance!
> > >
> > > --
> > > You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> > > To post to this group, send email to puppet...@googlegroups.com (javascript:) (mailto:puppet...@googlegroups.com (javascript:)).
> > > To unsubscribe from this group, send email to puppet-users...@googlegroups.com (javascript:) (mailto:puppet-users...@googlegroups.com (javascript:)).
> > > For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.
> >
>
> --
> 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/-/LcHGaFjy9JkJ.
> To post to this group, send email to puppet...@googlegroups.com (mailto:puppet...@googlegroups.com).
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com (mailto:puppet-users...@googlegroups.com).

devzero2000

unread,
Aug 12, 2012, 9:38:11 AM8/12/12
to puppet...@googlegroups.com
On Sun, Aug 12, 2012 at 2:43 PM, Eric Shamow <er...@puppetlabs.com> wrote:
First off, it's important to distinguish import from include. They "feel" like similar concepts but they're not - import goes and physically loads a file. Include says "ensure that this class is part of this host's resource graph."
It is an import concept. It is very simple to think "include" in puppet == "include" in C. No, they are much different 

It's important not to think of Puppet in procedural, top-down terms. We haven't just said, "give me all the stuff in this class, and now do these things." Rather we are building a model of a server, and manifest code simply informs pieces of that model. So by saying "include <class>" you are really saying, "Puppet, if you haven't already, make sure this class is part of the overall resource graph."

The better model for this example is, rather than a box inside of a box, you are looking at two sealed boxes sitting side by side. When you include the first group, it is added to the list of boxes used. Within the first box, when you then say "give me the second box," that second box is placed *alongside* the first. Thus in order to grab anything from it, you have to specify "I need the stuff from box 2."

Does that analogy help a bit more?

The reason for this is that, as your number of classes grows, so too will your variables. If they share a single global namespace, the likelihood of a global variable name being used more than once increases. This can lead to really unexpected changes and ambiguity in building the graph.

An example: suppose is this module you say $puppetmaster = "myserver.local" and in another module, you want to only run on a puppet master and so say $puppetmaster = true. When Puppet assembles the model of your system, which of the two is to be applied where? Dynamic scoping tried to guess at this. The idea here is to say, eliminate ambiguity - tell us exactly which one you want.

In fact, this is the best analogy.  But clearly it is easy for everyone to make mistakes especially if you are following the examples of "Pro Puppet" - incidentally, this book contains the same configuration discussed in this thread that was valid for puppet 2.6 but it is not anymore for puppet 2.7 

Best Regards
To post to this group, send email to puppet...@googlegroups.com.
To unsubscribe from this group, send email to puppet-users...@googlegroups.com.

Zachary Stern

unread,
Aug 13, 2012, 11:31:58 AM8/13/12
to puppet...@googlegroups.com
Ok, that's helpful, however:


On Sun, Aug 12, 2012 at 8:43 AM, Eric Shamow <er...@puppetlabs.com> wrote:
An example: suppose is this module you say $puppetmaster = "myserver.local" and in another module, you want to only run on a puppet master and so say $puppetmaster = true. When Puppet assembles the model of your system, which of the two is to be applied where? Dynamic scoping tried to guess at this. The idea here is to say, eliminate ambiguity - tell us exactly which one you want.
 
Sure, I get that, but I'm trying to do this within the module, not globally. 
Also imagine the following scenario.

I've configured the params.pp file to contain various things like $puppet::params:puppetserver - but what if I want to override that in a particular manfiest, but use the same template erb file for configuration. I can't, because I've used the scope.lookupvar tool in the manifest, rather than just doing <%= puppetserver %> and having that variable take on a value based on context - not in terms of "dynamic scoping" but in terms of being able to, as discussed, include params.pp, or define it inside the manifest that is using the template.

Is there any way to achieve this on-the-fly variable-changing, short of using hiera or something like that? I'm just not there yet.


--

zachary alex stern I systems architect | ceo - enter: narnia

o: 212.731.2033 | f: 212.202.6488z...@enternewmedia.com

60-62 e. 11th street, 4th floor | new york, ny | 10003

www.enternewmedia.com


Nan Liu

unread,
Aug 13, 2012, 12:33:24 PM8/13/12
to puppet...@googlegroups.com
On Mon, Aug 13, 2012 at 8:31 AM, Zachary Stern <z...@enternewmedia.com> wrote:
Ok, that's helpful, however:


On Sun, Aug 12, 2012 at 8:43 AM, Eric Shamow <er...@puppetlabs.com> wrote:
An example: suppose is this module you say $puppetmaster = "myserver.local" and in another module, you want to only run on a puppet master and so say $puppetmaster = true. When Puppet assembles the model of your system, which of the two is to be applied where? Dynamic scoping tried to guess at this. The idea here is to say, eliminate ambiguity - tell us exactly which one you want.
 
Sure, I get that, but I'm trying to do this within the module, not globally. 
Also imagine the following scenario.

I've configured the params.pp file to contain various things like $puppet::params:puppetserver - but what if I want to override that in a particular manfiest, but use the same template erb file for configuration. I can't, because I've used the scope.lookupvar tool in the manifest, rather than just doing <%= puppetserver %> and having that variable take on a value based on context - not in terms of "dynamic scoping" but in terms of being able to, as discussed, include params.pp, or define it inside the manifest that is using the template.

Is there any way to achieve this on-the-fly variable-changing, short of using hiera or something like that? I'm just not there yet.

This is typically modeled via:

class puppet (
  $server = $puppet::params::server
) include puppet::params {
...

This allows any variables be overwritten in declaration, while allowing the template to simply use $puppet::server. Most Puppet Labs forge modules use this pattern, so take a look at them if you need a more complex example. 

Nan

Zachary Stern

unread,
Aug 13, 2012, 12:37:43 PM8/13/12
to puppet...@googlegroups.com

class puppet (
  $server = $puppet::params::server
) include puppet::params {
...
}

If I'm already specifying scope, e.g. $server = $puppet::params::server, why do I also need the include? 

Nan Liu

unread,
Aug 13, 2012, 12:42:27 PM8/13/12
to puppet...@googlegroups.com
On Mon, Aug 13, 2012 at 9:37 AM, Zachary Stern <z...@enternewmedia.com> wrote:

class puppet (
  $server = $puppet::params::server
) include puppet::params {
...
}

If I'm already specifying scope, e.g. $server = $puppet::params::server, why do I also need the include? 

Sorry typo:

class puppet (
  $server = $puppet::params::server
) inherits puppet::params {
...

Nan

Zachary Stern

unread,
Aug 13, 2012, 12:45:40 PM8/13/12
to puppet...@googlegroups.com
On Mon, Aug 13, 2012 at 12:42 PM, Nan Liu <n...@puppetlabs.com> wrote:
class puppet (
  $server = $puppet::params::server
) inherits puppet::params {

Still missing something here - why do I need "inherits", or ANY statement at all, other than the $server = $puppet::params::server. $puppet::params::server seems like it should be specific enough.


Nan Liu

unread,
Aug 13, 2012, 1:52:36 PM8/13/12
to puppet...@googlegroups.com
You don't have access to variables in another class until you declare
or inherit from that class. This removes any ambiguity about the
values. This example might make it clearer why this is necessary:

class service::setting(
$enable
) {
if $enable {
$status = 'on'
} else {
$status = 'off'
}
}

So in this case the following manifests alone does not make any sense:

$status = $service::setting::status

The value of $service::setting::status can't be determined until the
class is declared:

class { 'service::setting':
enable => true,
}

$status = $service::setting::status

When puppet supports futures, this should give the same result:

$status = $service::setting::status

class { 'service::setting':
enable => true,
}

However, as of today, because puppet compilation is order dependent,
only the second example works.

Thanks,

Nan

Zachary Alex Stern

unread,
Aug 14, 2012, 11:56:53 AM8/14/12
to puppet...@googlegroups.com
Sorry typo:

class puppet (
  $server = $puppet::params::server
) inherits puppet::params {


FWIW, this works:

 class puppet::config {
  include puppet::params
  $puppetserver=$puppet::params::puppetserver
  $runinterval=$puppet::params::runinterval
  file { '/etc/puppet/puppet.conf':
    ensure => present,
    content => template('puppet/puppet.conf.erb'),

Chad Huneycutt

unread,
Aug 14, 2012, 1:44:50 PM8/14/12
to puppet...@googlegroups.com
On Tue, Aug 14, 2012 at 11:56 AM, Zachary Alex Stern
<z...@enternewmedia.com> wrote:
>
> FWIW, this works:
>
> class puppet::config {
>
> include puppet::params
> $puppetserver=$puppet::params::puppetserver
> $runinterval=$puppet::params::runinterval
> file { '/etc/puppet/puppet.conf':
> ensure => present,
> content => template('puppet/puppet.conf.erb'),
> require => Class['puppet::install'],
> notify => Class['puppet::service'],
> }
> }

So what if you would like to use a different puppet server? You have
to go in to the module and change the params.pp file? The code Nan
suggested allows you to override the value if necessary at the point
of inclusion, but fall back to a sane default specified in params.pp.
Personally, I am loving hiera, which provides even more flexibility:

class foo ( $bar = hiera('bar') ) {
...
}

And then $bar can be defined in a module-local class (if using puppet
hiera backend), module-local yaml file, site-specific yaml file,
overridden when included, etc, all according to a policy that you
define.

--
Chad M. Huneycutt

Zachary Stern

unread,
Aug 14, 2012, 2:12:12 PM8/14/12
to puppet...@googlegroups.com
That's a fair point, but not applicable to my use-case (multiple puppet servers).

However, at some point in the near future, I'll be using hiera, and that will be moot.

--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
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.


--

zachary alex stern I systems architect

jcbollinger

unread,
Aug 15, 2012, 9:51:57 AM8/15/12
to puppet...@googlegroups.com


On Tuesday, August 14, 2012 1:12:12 PM UTC-5, Zachary Alex Stern wrote:
That's a fair point, but not applicable to my use-case (multiple puppet servers).

However, at some point in the near future, I'll be using hiera, and that will be moot.

To use multiple Puppet masters for the same site and retain your sanity, it is essential that all site-wide classes and data be automatically synchronized across the masters.  If the multiple servers are simply for scaling / load balancing, then obviously the entire Puppet config must be synchronized.  With these considerations in mind, I think the objection about modifying manifests is a red herring: if there is a synchronization issue there then it is an instance of a type of problem that should already have been solved.


John

Zachary Stern

unread,
Aug 15, 2012, 11:06:30 AM8/15/12
to puppet...@googlegroups.com
My point was that I don't use and won't be using multiple puppet masters.

Also, at my current level of puppet knowledge, Nan's code is basically meaningless to me :(


On Wed, Aug 15, 2012 at 9:51 AM, jcbollinger <John.Bo...@stjude.org> wrote:
To use multiple Puppet masters for the same site and retain your sanity, it is essential that all site-wide classes and data be automatically synchronized across the masters.



Reply all
Reply to author
Forward
0 new messages