FAQ
I'm probably trying to solve a problem that I don't really need to
solve, but I'm hard-headed enough that I'm going ahead anyway...

I am using foo::params to centralize things like file paths, package
names, etc. that vary by $::osfamily, but I am running into
difficulties where the actual parameters might vary partly on
$::osfamily but within an OS family there might be other variations
not represented by facts.

Let's say I have a platform like Solaris where a particular software
package may be provided by several different parties and where I might
have perverse reasons for wanting to vary the package source-provider
on a system-by-system basis. Take Apache, for example: on one host
that's doing simple static file serving, I might use the 2.0 build
that is included with Solaris--minimizes dependencies, patches come
from OS vendor, etc.; on another Solaris box, a developer wants
something more complicated, so the 2.2 build from OpenCSW is needed.
And another developer wants Apache from Sunfreeware.

If stupid internal politics do not seem like an adequate
justification, let's say I'm building a module that I intend to
distribute via Puppet Forge, where I shouldn't just assume that
everyone will want his package from one vendor or the other.

How do you solve this?

Parameterized classes seem like the obvious answer, but what I've come
up with introduces an ordering dependency:

# init.pp
class testmod {
include testmod::params

info("\$testmod::params::whosit is '${testmod::params::whosit}'")
}

# params.pp
class testmod::params ($whosit = "foo") {
info("\$whosit got a '$whosit'")
}

This works:

class { 'testmod::params': whosit => 'barbarbar' }
class { 'testmod': } # or include testmod

But this results in "Duplicate declaration: Class[Testmod::Params] is
already declared":

class { 'testmod': }
class { 'testmod::params': whosit => 'barbarbar' }

as does node inheritance:

node basenode {
include testmod
}

node /./ inherits basenode {
class { 'testmod::params': whosit => 'burburbur' }
}

The best thing that I've come up with so far is to parameterize the
top-level class:

class testmod ($whosit = "foo") {
class { 'testmod::params': whosit => $whosit }
...
}

Which seems fine if the top-level class is the only thing to use the
testmod::params class, but that's unlikely in real life--I will
probably have a testmod::install class that uses the package name, a
testmod::service the uses the service name, etc.























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

Search Discussions

  • Jcbollinger at Jun 1, 2012 at 1:09 pm

    On May 31, 6:39 pm, Wil Cooley wrote:
    I'm probably trying to solve a problem that I don't really need to
    solve, but I'm hard-headed enough that I'm going ahead anyway...

    I am using foo::params to centralize things like file paths, package
    names, etc. that vary by $::osfamily, but I am running into
    difficulties where the actual parameters might vary partly on
    $::osfamily but within an OS family there might be other variations
    not represented by facts.

    Let's say I have a platform like Solaris where a particular software
    package may be provided by several different parties and where I might
    have perverse reasons for wanting to vary the package source-provider
    on a system-by-system basis. Take Apache, for example: on one host
    that's doing simple static file serving, I might use the 2.0 build
    that is included with Solaris--minimizes dependencies, patches come
    from OS vendor, etc.; on another Solaris box, a developer wants
    something more complicated, so the 2.2 build from OpenCSW is needed.
    And another developer wants Apache from Sunfreeware.

    If stupid internal politics do not seem like an adequate
    justification, let's say I'm building a module that I intend to
    distribute via Puppet Forge, where I shouldn't just assume that
    everyone will want his package from one vendor or the other.

    How do you solve this?

    Parameterized classes seem like the obvious answer, but what I've come
    up with introduces an ordering dependency:

    At least through Puppet 2.7.x, parameterized classes are rarely the
    best solution for anything (but things are supposed to be better in
    Puppet 3, so stay tuned). Ordering issues are but one of their common
    problems.

    # init.pp
    class testmod {
    include testmod::params

    info("\$testmod::params::whosit is '${testmod::params::whosit}'")

    }

    # params.pp
    class testmod::params ($whosit = "foo") {
    info("\$whosit got a '$whosit'")

    }

    This works:

    class { 'testmod::params': whosit => 'barbarbar' }
    class { 'testmod': } # or include testmod

    But this results in "Duplicate declaration: Class[Testmod::Params] is
    already declared":

    Yes. That's not an ordering issue, actually, it's another, more
    fundamental problem with parameterized classes pre-3.0: you can only
    include a parameterized class once per catalog. In fact, that's my
    pet complaint about parameterized classes, and the reason I don't even
    consider using them myself.

    class { 'testmod': }
    class { 'testmod::params': whosit => 'barbarbar' }

    as does node inheritance:

    node basenode {
    include testmod

    }

    node /./ inherits basenode {
    class { 'testmod::params': whosit => 'burburbur' }

    }

    The best thing that I've come up with so far is to parameterize the
    top-level class:

    class testmod ($whosit = "foo") {
    class { 'testmod::params': whosit => $whosit }
    ...

    }

    Which seems fine if the top-level class is the only thing to use the
    testmod::params class, but that's unlikely in real life--I will
    probably have a testmod::install class that uses the package name, a
    testmod::service the uses the service name, etc.

    Exactly. The Puppet 2 implementation of parameterized classes is best
    used in an environment where class inclusion and order is under strict
    external control, such as by an ENC that declares *every* needed
    class, in the order they need to be parsed. Such a setup demands that
    classes not include their own dependencies, but rather count on that
    externally-driven ordering to be right. Awful, really.

    If we take parameterized classes off the table, and if we reject node
    variables (which won't support this use case in Puppet 3), three
    possibilities remain:

    1) Make your ::params class smarter. Code the needed logic directly
    into it via conditionals.
    2) Declare the needed data as global variables. Use an ENC or
    conditional statements at the top of site.pp to set them
    appropriately.
    3) Load your data via an external data service, such as hiera.

    Option (1) is not well regarded, as it spreads data and logic
    throughout your manifest set and it makes your modules highly specific
    to your particular site. Module re-usability is right out the window.

    Option (2) is better, especially if you're using an ENC. It can be
    very quick to set up, and it's not too bad for re-usability so long as
    you document all the globals your module uses.

    Option (3) is a bit harder to set up if you're not already using hiera
    (or extlookup()), but it is very flexible and great for module re-use,
    it works nicely whether you use an ENC or not, and it is aligned with
    Puppet's development direction. Hiera is built in to Puppet 3, and
    integrated there with parameterized classes in a way that might
    finally make those usable.


    John

    --
    You received this message because you are subscribed to the Google Groups "Puppet Users" group.
    To post to this group, send email to puppet-users@googlegroups.com.
    To unsubscribe from this group, send email to puppet-users+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.
  • Jeff McCune at Jun 1, 2012 at 2:43 pm

    On Thu, May 31, 2012 at 4:39 PM, Wil Cooley wrote:

    I'm probably trying to solve a problem that I don't really need to
    solve, but I'm hard-headed enough that I'm going ahead anyway...

    I am using foo::params to centralize things like file paths, package
    names, etc. that vary by $::osfamily, but I am running into
    difficulties where the actual parameters might vary partly on
    $::osfamily but within an OS family there might be other variations
    not represented by facts.

    Let's say I have a platform like Solaris where a particular software
    package may be provided by several different parties and where I might
    have perverse reasons for wanting to vary the package source-provider
    on a system-by-system basis. Take Apache, for example: on one host
    that's doing simple static file serving, I might use the 2.0 build
    that is included with Solaris--minimizes dependencies, patches come
    from OS vendor, etc.; on another Solaris box, a developer wants
    something more complicated, so the 2.2 build from OpenCSW is needed.
    And another developer wants Apache from Sunfreeware.
    ... Or CoolStack or WebStack ... oh, Solaris...

    If stupid internal politics do not seem like an adequate
    justification, let's say I'm building a module that I intend to
    distribute via Puppet Forge, where I shouldn't just assume that
    everyone will want his package from one vendor or the other.

    How do you solve this?
    Honestly, these feel like different modules to me primarily because they're
    very different things on Solaris. Some of those apaches may have SMF
    manifests, some may not, the paths are going to vary wildly, etc..

    I think doing all of this in one module will make the module overly
    complex. The majority of the code will be dealing with "What Apache
    instance am I really managing here?" Unless you use defined resources, it
    will also be difficult to manage multiple copies of Apache on the same node
    with this module.

    If you can get it down to variations of the source and provider parmeters
    of one or more package resources then you can probably get away with a
    single module, but even then I'd recommend multiple classes to make things
    clear and easier to understand.

    -Jeff

    --
    You received this message because you are subscribed to the Google Groups "Puppet Users" group.
    To post to this group, send email to puppet-users@googlegroups.com.
    To unsubscribe from this group, send email to puppet-users+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.
  • Wil Cooley at Jun 1, 2012 at 5:08 pm

    On Jun 1, 7:42 am, Jeff McCune wrote:

    Honestly, these feel like different modules to me primarily because they're
    very different things on Solaris.  Some of those apaches may have SMF
    manifests, some may not, the paths are going to vary wildly, etc..
    That's an interesting twist--we've generally been organizing modules
    based
    on a vague notion of distinct services or system facilities (with
    concomitant
    ambiguities for very general or very minor ones, like "Should logadm/
    logrotate
    be part of the syslog module or in (a) separate module(s)?" and "Where
    should a refresh-only exec to reload init go?") and leaving it up
    to ::params to
    get these parts right for each $::osfamily, since we routinely deal
    with these
    same variations but based exclusively on facts rather than admin's
    whim.
    I think doing all of this in one module will make the module overly
    complex.  The majority of the code will be dealing with "What Apache
    instance am I really managing here?"  Unless you use defined resources, it
    will also be difficult to manage multiple copies of Apache on the same node
    with this module.
    Apache might have also been a poor example, since the legion of Apache
    modules on PF (and the complexity of our own internal module) attests
    to
    the inevitable incompleteness for a sufficiently complex service. Or
    maybe it
    was a really good example.

    In fact I was starting a Samba module and vaguely assuming that there
    were possibly other packages than those from OpenCSW that were in use
    somewhere in the world. (And I wonder why things take so long...) By
    comparison, Samba is relatively simple--one config File, one or two
    Services,
    three different package combinations, etc.

    The conclusion that I am drawing based on your and jcbollinger's
    responses
    is that there is not an obvious and correct way to do this and, on my
    part, I am
    just solve my immediately needs and not solve the general case right
    now.

    Thanks!
    Wil

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

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppuppet-users @
categoriespuppet
postedMay 31, '12 at 11:39p
activeJun 1, '12 at 5:08p
posts4
users3
websitepuppetlabs.com

People

Translate

site design / logo © 2022 Grokbase