On Friday, September 19, 2014 7:37:57 PM UTC-5, Nan Liu wrote:
TLDR summary: > > 1. include/require class does not provide containment. > 2. anchor is just a empty resource for containment. > 3. contain class provides containment without the need for anchor > 4. contain may cause unnecessary relationship, so use it only when > appropriate > 5. The original purpose for inherits, resource parameter override, is > rarely used. These days it's mostly a hack to have module data. classes > inherit params class which only contain puppet variables and no resources. > (I won't get into this and only focus on the previous points) > > On Fri, Sep 19, 2014 at 4:01 PM, Mike Reed <mjohn...@gmail.com > <javascript:>> wrote: > >> Hello all, >> >> I have a question about class and subclass relationships and what >> is/isn't the ideal way to go about such a thing. Please bear with me as >> I'm still refining my understanding of containment. Let's say I have a >> puppet module which manages the install of puppet and has the following >> pieces (currently using puppet v.3.4.3): >> >> *init.pp* >> class puppet { >> # evaluate supporting classes >> include puppet::params >> include puppet::config >> include puppet::service >> >> anchor { 'puppet::begin' : } -> >> class { '::puppet::params' : } -> >> class { '::puppet::config' : } ~> >> class { '::puppet::service' : } -> >> anchor { 'puppet::end' : } >> } >> >> *params.pp* >> class puppet::params { >> # puppet general params >> $puppet_path = '/etc/puppet' >> $puppet_config_template = 'puppet/puppet.conf.erb' >> $puppet_package = 'puppet' >> $puppet_common_package = 'puppet-common' >> $puppet_service_ensure = 'running' >> $puppet_service_enable = true >> $puppet_prod_version = '3.6.2-1puppetlabs1' >> $puppet_dev_version = '3.6.2-1puppetlabs1' >> validate_string($puppet_path) >> validate_string($puppet_config_template) >> validate_string($puppet_package) >> validate_string($puppet_common_package) >> validate_string($puppet_service_ensure) >> validate_bool($puppet_service_enable) >> validate_string($puppet_prod_version) >> validate_string($puppet_dev_version) >> > > validate don't serve any purpose here, since you setting these values to a > specific value. > > >> } >> >> *config.pp* >> class puppet::config ( >> >> $puppet_config_path = $::puppet::params::puppet_config_path, >> $puppet_config_template = $::puppet::params::puppet_config_template, >> $puppet_service = $::puppet::params::puppet_service, >> >> ) inherits puppet::params { >> >> file { 'puppet.conf' : >> ensure => present, >> path => "${puppet_config_path}/", >> content => template("${puppet_config_template}"), >> notify => Service["${puppet_service}"], >> } >> } >> >> *service.pp* >> class puppet::service ( >> >> $puppet_package = $::puppet::params::puppet_package, >> ***truncated variables for sake of a long post*** >> >> ) inherits puppet::config { >> >> package { "${puppet_package}": >> ensure => "${puppet_prod_version}", >> } >> >> package { "${puppet_common_package}": >> ensure => "${puppet_prod_version}", >> } >> >> service { "${puppet_service}": >> ensure => "${puppet_service_ensure}", >> name => "${puppet_service}", >> enable => "${puppet_service_enable}", >> hasrestart => true, >> hasstatus => true, >> subscribe => Package["${puppet_config_template}"], >> } >> } >> >> Based on the above, I've left a few things which I feel don't belong but >> for the sake of my questions, they're included. >> >> Per the above init.pp, I've added an anchor to force ordering. My >> understanding is that this has nothing to do with application-order and >> more to do with parse-order. With that said, I have a few questions: >> > > > The anchor is not parse-order, it to server as a ghost containment > resource for the class. > I re-emphasize that point. The Anchor pattern has NOTHING to do with parse / evaluation order, exactly contrary to the OP's assertion. It is only relevant to client-side order of resource application. > The syntax short hand is what confusing. You can totally rewrite as > following: > > class { '::puppet::params' : require => Anchor['puppet::begin']} > class { '::puppet::config' : } ~> > class { '::puppet::service' : before => Anchor['puppet::end']} > anchor { ['puppet::begin', 'puppet::end']: } > > Although that does have exactly the same application-order implications as the OP's version, it has slightly different evaluation / catalog-building semantics, owing to the use of resource-like class declarations in place of 'include' / 'require' / 'contain'. That probably does not matter for this example, but it might matter in some other cases. > 1. By adding the 'include' in init.pp, my understanding is that simply >> says to 'evaluate' the subclasses but does not indicate an order to which >> subclasses are to be applied. Is that correct? >> > > Mostly. > > >> 2. I think the 'inherits' function is depreciated but should each >> instance be replaced with a 'contain' (based on the order I want) >> throughout my subclass manifests? My understanding is that I should never >> 'contain' more than one subclass within the same module as puppet will be >> confused on ordering. >> > > The inherits params class data pattern can't be replaced by contain. > contain is a new function that is used to eliminate the need for anchor. > The inherits params class data pattern cannot be replaced by contain / require / include because its purpose it to ensure that the parent class is evaluated *before any part of the child class*, and before the child class's parameter list in particular. This exploits a side effect of class inheritance -- it is not the real purpose of class inheritance (which purpose is little used any more). If you don't need the variables of the params class for class parameter defaults, however, then you can replace inheritance with 'include'ing (or 'require'ing or 'contain'ing) the params class it the start of erstwhile child class's body. And specifically to the OP's point, no, Puppet will not be confused by one class 'contain'ing multiple classes. It might, however, be confused by multiple classes all 'contain'ing the same class. That confusion would likely manifest in the form of an error complaining about a relationship cycle. > > 3. I rather like the idea of the anchor in the init.pp because I only >> have one place to go to, in order to see the relationship of the >> subclasses. With the introduction of the 'contain' feature, I feel like the >> anchor is no longer needed; however, is there a preferred way of ordering >> these subclasses without using the anchor pattern? >> > > Anchor is not intended to order subclass, it's suppose to help other class > calling this class to have containment. > That's an odd way to phrase it. I'd put it like this: the purpose of the Anchor pattern and of 'contain' is to allow the class employing it to provide containment of the classes it declares. I add also that such containment is only sometimes needed or wanted. > If you want your internal class to have order just do: > > class { '::puppet::params' : } -> > class { '::puppet::config' : } ~> > class { '::puppet::service' :} > > Or alternatively, if you declare the classes via 'contain' then you can specify order of application via resource references: contain '::puppet::params' contain '::puppet::config' contain '::puppet::service' Class['::puppet::params'] -> Class['::puppet::config'] ~> Class['::puppet::service'] > If the calling class use 'contain puppet' instead of 'include puppet' you > don't need the anchors. > > Unfortunately this is a confusing subject. I'll provide some examples and > step through them: > > 1. include/require class does not provide containment for classes with no > resource. > > You would expect the following code to work: > > class software { > include 'software::install' > include 'software::config' > include 'software::service' > Class['software::install'] -> Class['software::config'] ~> > Class['software::service'] > } > > node example{ > include('repo', 'software', 'postinstall') > Class['repo'] -> Class['Software'] -> Class['postinstall'] > } > > Unfortunately, because software does not have any resource, > > Puppet does not translate the relationship to: > Class['repo'] -> Class['software::install'] -> Class['software::config'] > ~> Class['software::service'] -> Class['postinstall'] > For the record, whether Class['software'] declares any resources is not (directly) a factor there. Relationships between classes are effectively forwarded to the ordinary resources declared by those classes, but *not* to other classes declared by those classes. We say that classes "contain" the ordinary resources they declare, but not the classes they declare (other than those they declare via the 'contain' function). That's useful and intentional, but sometimes you want classes to also contain the other classes they declare. > > This is because include doesn't offer containment around classes (that > nest other classes). Puppet in this cause behaves like: > Class['repo'] -> Class['postinstall'] > It still enforces the following, but not in any relation with the other > class: > Class['software::install'] -> Class['software::config'] ~> > Class['software::service'] > > > 3. contain class provides containment without the need for anchor. > > class software { > include 'software::install' > include 'software::config' > include 'software::service' > I think Nan meant to use 'contain' instead of 'include' in the three lines above. If not, then it's still what he should have done :-) > Class['software::install'] -> Class['software::config'] ~> > Class['software::service'] > } > > node example { > contain('repo', 'software', 'postinstall') > I think that's a little confused. I'm not sure whether 'contain' would work when called directly from a node block, but if it did then the only reasonable result (causing the specified classes to be contained by the node) would be useless, because there is no way to express ordering constraints involving whole node blocks. > Class['repo'] -> Class['Software'] -> Class['postinstall'] > } > > Now puppet will translate this to: > Class['repo'] -> Class['software::install'] -> Class['software::config'] > ~> Class[''software::service'] -> Class['postinstall'] > > In this case containment avoided the need for anchor resources. > With class 'software' rather than the node block doing the containing, yes, that would be the result. > > 4. However use containment cautiously since it may introduce unnecessary > relationship. > > Yes, but not any relationships inconsistent with those produced by applying the Anchor pattern. You can replace the Anchor pattern with 'contain'ing the erstwhile anchored classes without risk of creating (new) relationship cycles. That may nevertheless produce more relationships overall, however, which incrementally increases Puppet's work. I think, though, that Nan's point was actually that it is not necessarily safe for just any class to 'contain' any other class. Sometimes non-containment semantics are exactly what you want. > class software { > contain repo > } > > behaves the same as: > class software { > include repo > anchor { ['software::start', 'software::end']: } > Anchor[software::start] -> Class['repo'] -> Anchor['software::end'] > } > > Here's a simple example why you shouldn't use contain instead of include > carelessly: > > class repo { } > > class software_a { > contain repo > } > > class software_b { > contain repo > } > > include software_a, software_b > Class[software_a] -> Class[software_b] > > I'll leave it as an exercise for you to work out how to get the final > example working. > > I won't give away the solution to the exercise, but the take-home point is that you should use 'contain' (or the Anchor pattern) only where you really mean that the contained class is, for all intents and purposes, part of the containing class. Where you *do* mean that, do use 'contain'. John -- You received this message because you are subscribed to the Google Groups "Puppet Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/cd9e0cce-deca-48f1-aa42-5197035f3461%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.