Best practice question: Class(es) with client/server config

64 views
Skip to first unread message

Joachim Schrod

unread,
Apr 24, 2014, 8:35:10 PM4/24/14
to puppet...@googlegroups.com
Hi,

I've got question about best practice, and am interested in advice
from you experienced folk.

I have a service where there is one server in my net and many
clients (actually, all systems are clients): CUPS. In client
setups, one file has to be changed (client.conf), in server setup
several (cupsd.conf with policy, several printers are installed).
On the server, client.conf must not be changed.

Is it better to create 2 clases (cupsclient, cupsserver) or one
class (cups)?

One class would mean to set a role (client, server) via Hiera and
distinguish the target configuration via that role. I.e., within
the class one giant if with two completely different target
configurations. Doesn't sound pretty or good style.

When I use two classes I have the problem how to specify when the
right class should be declared: What I would like to have is a
method to declare cupsclient for all nodes, and override that
declaration for the one node/role that's the server where it should
be cupsserver. I can't see how I do that properly. (Most class
declarations are in role files, roles are assigned to nodes via
Hiera. I don't want to add cupsclient declaration to any and all
roles except that server role, that repetition is the reason that I
don't like this approach.)

So I see two approaches, both with deficiencies.
What approach do I miss?
How can one of these approaches be changed to get rid of the
described deficiency?

Can I specify the class name to be declared as a parameter in Hiera?

Thanks in advance for any answer,

Joachim

PS: The distinction of node/role is not relevant in that use case.
That server exists only once, it factually _is_ its own role.

--
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Joachim Schrod, Roedermark, Germany
Email: jsc...@acm.org

Antoine Cotten

unread,
Apr 25, 2014, 7:49:44 AM4/25/14
to puppet...@googlegroups.com, jsc...@acm.org
I don't pretend to give you best practices here, but I would personally create one cups class as an entry point, with two (at least) boolean parameters: client and server.
client defaults to true and server to false, either using class defaults or Hiera's base hierarchy level.
Then you could imagine having two subclasses cups::client and cups::server which do the things you want. In your main class, you just have to call either of them using a conditional statement.

With this approach you can apply the cups class safely to any node by default, and override the server boolean just for your CUPS server in either the Hiera hierarchy or site.pp.

Toni

Felix Frank

unread,
Apr 25, 2014, 8:34:51 AM4/25/14
to puppet...@googlegroups.com
That'll work, and not at all a bad design choice. But the parameterised
cups wrapper class is ultimately unnecessary - you can just have all
nodes include cups::client and make sure that the server(s) also include
cups::server in a fashion that suits your general manifest design.

Talking roles/profiles, you'd make sure that the cups::client profile is
present in all roles, but that the cups-server role also include
cups::server.

Do move shared resources to a cups::common class.

Cheers,
Felix

jcbollinger

unread,
Apr 25, 2014, 10:14:04 AM4/25/14
to puppet...@googlegroups.com


On Thursday, April 24, 2014 7:35:10 PM UTC-5, Joachim Schrod wrote:
Hi,

I've got question about best practice, and am interested in advice
from you experienced folk.

I have a service where there is one server in my net and many
clients (actually, all systems are clients): CUPS. In client
setups, one file has to be changed (client.conf), in server setup
several (cupsd.conf with policy, several printers are installed).
On the server, client.conf must not be changed.

Is it better to create 2 clases (cupsclient, cupsserver) or one
class (cups)?



It is better to have two classes, as there is essentially no commonality between the resource to be managed in the one case and those to be managed in the other.  Furthermore, what if you ever wanted the same machine to be both the server and one of its own clients?  I'm not sure whether that is feasible for CUPS specifically, but as a general manifest design principle it is quite sound.

To the extent that there is any commonality at all, it should be factored out into some other class (e.g. Felix's cups::common) that both cups::client and cups::server declare.


When I use two classes I have the problem how to specify when the
right class should be declared: What I would like to have is a
method to declare cupsclient for all nodes, and override that
declaration for the one node/role that's the server where it should
be cupsserver. I can't see how I do that properly. (Most class
declarations are in role files, roles are assigned to nodes via
Hiera. I don't want to add cupsclient declaration to any and all
roles except that server role, that repetition is the reason that I
don't like this approach.)


Why shouldn't you add cups::client to every role that needs it, and avoid adding it to those roles that don't need it?  Although you currently want all nodes to be either CUPS clients or a (the) CUPS server, it is conceivable that you may someday want to manage machines that are neither (e.g. Windows or Mac clients).  And where else do you have in mind to put the needed declaration anyway?

 

So I see two approaches, both with deficiencies.
What approach do I miss?
How can one of these approaches be changed to get rid of the
described deficiency?

Can I specify the class name to be declared as a parameter in Hiera?



Sure you can, and that's a fine way to proceed.  (And isn't that what you are doing already to assign role classes to nodes via Hiera?)  For example, your data might contain this:

common.yaml
----
cups_class: cups::client


my.cups.server.yaml
----
cups_class: cups::server


Then, in some suitable manifest you could write

$cups_class = hiera('cups_class')
include $cups_class


Or you could even shorten that to

hiera_include('cups_class')


John

Joachim Schrod

unread,
Apr 26, 2014, 12:06:51 PM4/26/14
to puppet...@googlegroups.com
On 04/25/14 14:34, Felix Frank wrote:
> That'll work, and not at all a bad design choice. But the parameterised
> cups wrapper class is ultimately unnecessary - you can just have all
> nodes include cups::client and make sure that the server(s) also include
> cups::server in a fashion that suits your general manifest design.
>
> Talking roles/profiles, you'd make sure that the cups::client profile is
> present in all roles, but that the cups-server role also include
> cups::server.

A cups server must not be a cups client as well. When CUPS client
configuration is installed, the server configuration is ignored.

Joachim

Joachim Schrod

unread,
Apr 26, 2014, 12:45:20 PM4/26/14
to puppet...@googlegroups.com
On 04/25/14 16:14, jcbollinger wrote:
> On Thursday, April 24, 2014 7:35:10 PM UTC-5, Joachim Schrod wrote:
>
> I have a service where there is one server in my net and many
> clients (actually, all systems are clients): CUPS. In client
> setups, one file has to be changed (client.conf), in server setup
> several (cupsd.conf with policy, several printers are installed).
> On the server, client.conf must not be changed.
>
> Is it better to create 2 clases (cupsclient, cupsserver) or one
> class (cups)?
>
> It is better to have two classes, as there is essentially no
> commonality between the resource to be managed in the one case and
> those to be managed in the other.

As became hopefully clear in the rest of my post, I saw that, too;
but didn't know how to realize it. You showed me the hint at the
end of your email; nevertheless I'd like to answer to a few remarks
of you.

> Furthermore, what if you ever
> wanted the same machine to be both the server and one of its own
> clients?

That would be easy, then I'd have a client configuration that would
be applied everywhere and a server configuration just at one node.

The situation with CUPS is that a server MUST NOT be configured as
a client. (As I wrote above.) When a client configuration is
established, the server configuration is ignored. Client and server
configurations are exclusive.

> When I use two classes I have the problem how to specify when the
> right class should be declared: What I would like to have is a
> method to declare cupsclient for all nodes, and override that
> declaration for the one node/role that's the server where it
> should
> be cupsserver. I can't see how I do that properly. (Most class
> declarations are in role files, roles are assigned to nodes via
> Hiera. I don't want to add cupsclient declaration to any and all
> roles except that server role, that repetition is the reason
> that I
> don't like this approach.)
>
> Why /shouldn't/ you add cups::client to every role that needs it,
> and avoid adding it to those roles that don't need it? Although
> you currently want all nodes to be either CUPS clients or a (the)
> CUPS server, it is conceivable that you may someday want to manage
> machines that are neither (e.g. Windows or Mac clients). And where
> else do you have in mind to put the needed declaration anyway?

I want to put the declaration in commons service block that is
declared for all profiles. Every profile needs CUPS printing, there
is just a different module/resource variant for one role.

This is not an installation where a role is installed on many nodes
at the same time; most roles are installed once, maybe twice or at
most on three systems. Since I have many roles, having to repeat
something basic like "printing is activated" in all of them looks
clumsy to me.

When I will have the case that Windows system configuration must be
also supported by that Puppet installation; I'll reconsider. (Mac
uses CUPS as well... ;-)) Until then, I withgo premature
abstractions that I don't need.

>
> Can I specify the class name to be declared as a parameter in
> Hiera?
>
> Sure you can, and that's a fine way to proceed.

Thanks for your example; it was exactly what I needed to go
further. It works now.

> (And isn't that
> what you are doing already to assign role classes to nodes via
> Hiera?)

I assign role classes by listing them as an element in a classes
list attribute in Hiera files. (I've got hiera_include('classes')
in site.pp.) Don't know if that's good or bad for maintenance; as I
wrote, I'm just starting to use Puppet.

I don't even know yet if a pure node/role/profile/module pattern is
really appropriate for me, as most node/role relationships are 1:1
and not n:1. The role abstraction layer has maintenance costs
associated where the gains in an environment like mine is not
really clear. Maybe skipping the role abstraction, and associating
profiles to nodes via Hiera is a better way to go; I'll have to
experiment and find out. (My intent for using Puppet is not
maintenance of many systems. It is configuration of a few systems
that is documented, version controlled, and where installation of
replacement systems is fast and repeatable.)

Best, and thanks for the hint that brought me a working config,

Joachim
Reply all
Reply to author
Forward
0 new messages