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.
On May 31, 6:39 pm, Wil Cooley <wilcoo...@gmail.com> 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.
On Thu, May 31, 2012 at 4:39 PM, Wil Cooley <wilcooley@gmail.com> 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.
On Jun 1, 7:42 am, Jeff McCune <j...@puppetlabs.com> 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.