Exporting a resource only once....

116 views
Skip to first unread message

Krist van Besien

unread,
Jan 23, 2014, 7:56:41 AM1/23/14
to puppet...@googlegroups.com
Hello,


I have a need for an exported resource that only gets exported once. So a class that exports this resource should test first if it hasn't already been exported, and only then export it.

In pseudo code:

if
  !exists X
then
  @@X


The problem is that I can't seem to find out how to test for the existence of an exported resource in puppet, without any side effects...

Any hints?

Krist


jcbollinger

unread,
Jan 23, 2014, 1:59:29 PM1/23/14
to puppet...@googlegroups.com


Yes: don't do that.

It only makes sense to export resources that are somehow specific to or characteristic of the node whose catalog is being compiled.  If you have such a resource to export, then structure your manifests so that no more than one declaration of the resource can be evaluated during any catalog compilation.  No need for any tests in that case.  If different nodes may export similar resources, then do ensure that they all have distinct titles (and names) across all nodes.

On the other hand, if your resources are not characteristic of any particular node, then they are not suited for export.  We might be able to suggest specific alternatives if you explained what you are trying to achieve in more detail.


John

Krist van Besien

unread,
Jan 23, 2014, 4:26:08 PM1/23/14
to puppet...@googlegroups.com


On Thursday, January 23, 2014 2:59:29 PM UTC+1, jcbollinger wrote:

It only makes sense to export resources that are somehow specific to or characteristic of the node whose catalog is being compiled.  If you have such a resource to export, then structure your manifests so that no more than one declaration of the resource can be evaluated during any catalog compilation.  No need for any tests in that case.  If different nodes may export similar resources, then do ensure that they all have distinct titles (and names) across all nodes.

On the other hand, if your resources are not characteristic of any particular node, then they are not suited for export.  We might be able to suggest specific alternatives if you explained what you are trying to achieve in more detail.


Basically my situation is the following:
- A database server
- Several web application servers.

The whole managed using foreman/puppet

My Web applications each need a database, so I would like to just export on the web application nodes the databases I need, and collect them on the database server. However, several nodes that run the same web application of course need the same database. What do I do when I have two nodes, that both need the same database?
The logical, intuitive solution would be to export it on all of them, but only collect it once on the database server.

Other situations are : backends to a loadbalancer that export both frontend and backend URLs. The loadbalancer collects both, and creates it's configuration based on them.

Krist










 

xav

unread,
Jan 23, 2014, 6:25:47 PM1/23/14
to puppet...@googlegroups.com
On Thu, 2014-01-23 at 08:26 -0800, Krist van Besien wrote:

>
>
>
> Basically my situation is the following:
> - A database server
> - Several web application servers.
>
> The whole managed using foreman/puppet
>
> My Web applications each need a database, so I would like to just
> export on the web application nodes the databases I need, and collect
> them on the database server. However, several nodes that run the same
> web application of course need the same database. What do I do when I
> have two nodes, that both need the same database?
> The logical, intuitive solution would be to export it on all of them,
> but only collect it once on the database server.
>
> Other situations are : backends to a loadbalancer that export both
> frontend and backend URLs. The loadbalancer collects both, and creates
> it's configuration based on them.
>
> Krist

We have something quite similar - as we use hiera extensively we managed
to have a common yaml file with a list of databases in a hash, and used
create_resources to create the databases (and users, and haproxy
listeners) on the database/haproxy nodes.

The application nodes that want to register with a load balancer export
resources for themselves only, which are collected on the load balancer
only.

An alternative is to have a manifest that ensures there is a suitable
database available, creating it if not, running on the web application
servers - you've got a db client there already which should be able to
access the db server. That approach also allows you to ensure there's a
database created before attempting to populate it and start the app,
exported resources mean you'll need several runs before everything is
clean.


Krist van Besien

unread,
Jan 24, 2014, 6:36:39 AM1/24/14
to puppet...@googlegroups.com


On Thursday, January 23, 2014 7:25:47 PM UTC+1, Xav Paice wrote:
 
We have something quite similar - as we use hiera extensively we managed
to have a common yaml file with a list of databases in a hash, and used
create_resources to create the databases (and users, and haproxy
listeners) on the database/haproxy nodes.

The application nodes that want to register with a load balancer export
resources for themselves only, which are collected on the load balancer
only.

We don't use Hiera. We use foreman as ENC. Thus we also don't have per node manifests.
What we do have is our own module, with classes we assign to host groups in foreman. We define a host group for or different categories of servers, and assign hosts to a host group based on what they are supposed to do.
So if we for example want to add another backend we just create a new host in foreman, add it to the right host group, and then flip a switch. It powers on, bootstraps itself, installs all it needs, and exports what it needs from other servers. However we run in to the problem of duplicate external resources, which I have for the moment resolved through some ugly hacks.

An alternative is to have a manifest that ensures there is a suitable
database available, creating it if not, running on the web application
servers - you've got a db client there already which should be able to
access the db server.  That approach also allows you to ensure there's a
database created before attempting to populate it and start the app,
exported resources mean you'll need several runs before everything is
clean.
 

For security reason we only allow root access to the mysql database from the host it runs on, via a unix socket. That is why the DB server needs to collect the databases and then create them.

But that problem seems solvable also. But I am still interested in a general solution-
We have "virtual resources". This has allowed me to for example declare all the different VLANs in one class that all nodes include, but then only realize the lans needed on a per service basis. Something like "exported virtual resources" would be convenient.
We also run in to trouble for example when dealing with users. We use a lot of the openstack classes on puppetforge. Some of those classes will try to create keystone user resources. Again these should be exported so the keystone server can collect and create them. But again, only once...







Lorenzo Salvadorini

unread,
Jan 24, 2014, 11:51:07 AM1/24/14
to puppet...@googlegroups.com
>Basically my situation is the following:
>- A database server
>- Several web application servers. 

We have a similar environment and the solution we adopted is very simple: put the "if ! defined" inside the exported resource and of course make the name of the resource unique between the web application servers.
All the webservers will export the resource database, the database host will collect them but the resource will be created only on the first occurrence in the catalog.
This is a general pattern that we use for mysql database, memcache instances, etc... 

My 2 c

jcbollinger

unread,
Jan 24, 2014, 2:25:52 PM1/24/14
to puppet...@googlegroups.com


On Thursday, January 23, 2014 10:26:08 AM UTC-6, Krist van Besien wrote:


On Thursday, January 23, 2014 2:59:29 PM UTC+1, jcbollinger wrote:

It only makes sense to export resources that are somehow specific to or characteristic of the node whose catalog is being compiled.  If you have such a resource to export, then structure your manifests so that no more than one declaration of the resource can be evaluated during any catalog compilation.  No need for any tests in that case.  If different nodes may export similar resources, then do ensure that they all have distinct titles (and names) across all nodes.

On the other hand, if your resources are not characteristic of any particular node, then they are not suited for export.  We might be able to suggest specific alternatives if you explained what you are trying to achieve in more detail.


Basically my situation is the following:
- A database server
- Several web application servers.

The whole managed using foreman/puppet

My Web applications each need a database, so I would like to just export on the web application nodes the databases I need, and collect them on the database server. However, several nodes that run the same web application of course need the same database. What do I do when I have two nodes, that both need the same database?
The logical, intuitive solution would be to export it on all of them, but only collect it once on the database server.



No, that's not sensible.  It presents largely the same problem as multiple concrete declarations of the same resource within one node's catalog.  All declarations of the resource must be kept in sync, and no one of them is authoritative.  Even if they all happen to be identical during any given catalog compilation, Puppet rejects the duplicates.

I imagine you saying that the declarations in your case are automatically in sync because all the nodes in question issue it via the same manifest.  If that's the case, however, then the declaration must not depend on anything specific to the nodes consuming the database, therefore it could just as easily be declared as a concrete resource on the database server.  That has the added advantages that
  1. The database can be configured before any of its consumers are
  2. The database does not fall out of management if its consumers go down / stop checking in
  3. It's possible to determine what databases are supposed to be configured strictly by looking at the manifests and data pertinent to the database server
 
Other situations are : backends to a loadbalancer that export both frontend and backend URLs. The loadbalancer collects both, and creates it's configuration based on them.
 


Back ends shouldn't be telling the LB what its front-end URL is.  That is and should remain a property of the LB itself.  On the other hand, it is quite reasonable for the back ends to export their own URLs for the LB to collect.  That's actually a pretty good example of what should be exported and what should not.


John

jcbollinger

unread,
Jan 24, 2014, 2:56:43 PM1/24/14
to puppet...@googlegroups.com


On Friday, January 24, 2014 12:36:39 AM UTC-6, Krist van Besien wrote:


On Thursday, January 23, 2014 7:25:47 PM UTC+1, Xav Paice wrote:
 
We have something quite similar - as we use hiera extensively we managed
to have a common yaml file with a list of databases in a hash, and used
create_resources to create the databases (and users, and haproxy
listeners) on the database/haproxy nodes.

The application nodes that want to register with a load balancer export
resources for themselves only, which are collected on the load balancer
only.

We don't use Hiera. We use foreman as ENC.


The two statements are not directly related -- Foreman and Hiera are not mutually exclusive.  I can understand, however, that you want to manage your site data all within the Foreman system.

 
Thus we also don't have per node manifests.


Few people do, I think, but I don't see how that's relevant.

 
What we do have is our own module, with classes we assign to host groups in foreman. We define a host group for or different categories of servers, and assign hosts to a host group based on what they are supposed to do.
So if we for example want to add another backend we just create a new host in foreman, add it to the right host group, and then flip a switch. It powers on, bootstraps itself, installs all it needs, and exports what it needs from other servers. However we run in to the problem of duplicate external resources, which I have for the moment resolved through some ugly hacks.


Your general approach seems sound.  Your duplicate resource problem arises from managing shared resources in the wrong place.  You have the model backwards in that regard: resource declarations should not be exported as a means of saying "I need this resource", but rather as a means of saying "I provide this resource".

The canonical example is Host resources, which manage entries in /etc/hosts.  If each node exports a Host resource for its own name and IP address, and also collects all Host resources, then you end up with each node having all managed nodes listed in its hosts file.  Each export says "My name is <my hostname>, and you can find me at <my address>."  The nodes provide a machine (themselves) with the given name at the given address.



An alternative is to have a manifest that ensures there is a suitable
database available, creating it if not, running on the web application
servers - you've got a db client there already which should be able to
access the db server.  That approach also allows you to ensure there's a
database created before attempting to populate it and start the app,
exported resources mean you'll need several runs before everything is
clean.
 

For security reason we only allow root access to the mysql database from the host it runs on, via a unix socket. That is why the DB server needs to collect the databases and then create them.



It is a good plan to allow root access only from the database host, but that in no way means database resources need to be collected into its catalog instead of being declared concretely there.  As a first, minimally-elegant approach, define a class for each database, and assign those classes to the DB server via Foreman, as appropriate.

Slightly more elegant might be to use a single class, driven by top-scope data defined via Foreman.  If you were willing to put your data in a place and form where the master could access it via Hiera then a rather more elegant solution could be constructed.

 
But that problem seems solvable also. But I am still interested in a general solution-
We have "virtual resources". This has allowed me to for example declare all the different VLANs in one class that all nodes include, but then only realize the lans needed on a per service basis. Something like "exported virtual resources" would be convenient.


Just like concrete resources, virtual resources may only be declared once within their scope (one catalog).  They may be realized / collected many times, but that's not the same thing at all.

Exported resources are just the same, except that their scope is the entire purview of a given master.  They may be collected many times, for one node or for many, but they may have only one declaration.  It's all quite consistent.


John

Krist van Besien

unread,
Jan 28, 2014, 10:36:07 AM1/28/14
to puppet...@googlegroups.com

That's an interesting solution. I'll try that out.

Krist
 
Reply all
Reply to author
Forward
0 new messages