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.

Reply via email to