Search code examples
puppetrolesprofiles

Puppet Roles/Profiles and init.pp class parametrized only


I would to apply the roles/profiles pattern as explained by Craig Dunn and at URL https://docs.puppet.com/pe/2016.4/r_n_p_full_example.html to my modules built with the rules specified at https://docs.puppet.com/guides/module_guides/bgtm.html.

Following these rules, every component modules (like Apache, OpenLDAP) keep only init.pp class parametrized.

#
# Component classes.
#

class apache ($logfile = '.log', $backend = $::apache::params::backend) inherits ::apache::params {
    #
    # This is the unique parametrized class.
    #
}

class apache::log inherits ::apache {
    #
    # This class inherits the params.
    #
}

Well, I usually use Roles/Profiles classes (like profile::apache, profile::openldap) using include-like or resource-like declaration of the component classes.

If I use resource-like declaration in a parent profile class (like profile::apache), and I use inherited profile (like profile::apache::openssh or profile::apache::openldap) to apply new features, i need to override the parent definition of component, but with Puppet v4 I can't override the component class declared in the parent profile (Class[::apache] { backend => 'ldap' } ) or use resource collector (Class <| name=='::apache'|> { backend => 'ldap' }).

#
# Profile classes.
#

class profile::apache {
    class { ::apache:
        logfile => '.my_log',
    }
}

class profile::apache::openldap inherits profile::apache {
    include ::openldap

    # Doesn't work with Puppet 4.
    Class[::apache] {
        backend => 'ldap'
    }

    #
    # OR...
    #

    # Also, doesn't work with Puppet 4.
    Class <| name=='::apache'|> {
        backend => 'ldap'
    }
}

In the past I used parametrized component classes and in the child profile classes I could call the component classes in the direct way without override the params of init.pp of component class.

This is my old approch:

class apache::backend ($backend) inherits ::apache {
    #
    # Do stuff.
    #
}

class profile::apache::openldap inherits profile::apache {
    include ::openldap

    class { ::apache::backend:
        backend => 'ldap'
    }
}

class profile::apache::mysql inherits profile::apache {
    include ::mysql

    class { ::apache::backend:
        backend => 'mysql'
    }
}

So, with my new style of coding, how can I apply the roles/profile pattern if I use only init.pp component class parametrized?


Solution

  • Puppet DSL is not an object-oriented language in the sense you seem to think it is. In particular, class inheritance does not do what you think. The language specification remarks:

    Class inheritance should be used very sparingly, generally only in the following situations:

    • When you need to override resource attributes in the base class.
    • To let a “params class” provide default values for another class’s parameters:

    [...] In nearly all other cases, inheritance is unnecessary complexity.

    In practice, only the latter use of inheritance is common.

    You are trying to modify class parameters of the parent class itself, which

    1. is not enabled by inheriting from that class (it is quite different from overriding the attributes of resources declared by that class), and
    2. does not and cannot work in Puppet in general.

    Indeed, the language guide also says (emphasis added):

    If a base class has parameters, those parameters must either have default values, or have their values supplied by automatic external data lookup. You can’t specify values in the Puppet language for parameters in an inherited class.


    When you say ...

    In the past I used parametrized component classes and in the child profiles I could call the component classes in the direct way without override the params of init.pp of component class.

    ... I suppose you mean that you used resource-like class declarations, so as to specify parameter values in your manifests. That's poor style in most cases, with the exception being a class declaring private classes of the same module.

    So, how can I apply the roles/profile pattern if I use only init.pp component class parametrized?

    As the language guide says, by relying on external data lookup (i.e. Hiera) to customize module parameters. Note also that this applies to component modules, too. Given the existence of public class apache::log that inherits from class apache, you should not declare the latter via the resource-like syntax.