6

The use case i try to illustrate is when to declare some item (eq mysqld service) with a default configuration that could be included on every node (class stripdown in the example, for basenode), and still be able to override this same item in some specific class (eg mysql::server), to be included by specific nodes (eg myserver.local)

I illustrated this use case with the example below, where i want to disable mysql service on all nodes, but activate it on a specific node. But of course, Puppet parsing fails because the Service[mysql] is included twice. And of course, class mysql::server bears no relation to be a child of class stripdown

Is there a way to override the Service["mysql"], or mark it as the main one, or whatever ? I was thinking about the virtual items and the realize function, but it only permits apply an item multiple times, not to redefine or override.

# In stripdown.pp :
class stripdown {
    service {"mysql": enable => "false", ensure => "stopped" }
}

# In mysql.pp :
class mysql::server {  
    service { mysqld:  
        enable      => true,  
        ensure      => running,  
        hasrestart  => true,  
        hasstatus   => true,  
        path        => "/etc/init.d/mysql",  
        require     => Package["mysql-server"],  
    }
}

# Then nodes in nodes.pp :
node basenode {
    include stripdown
}

node myserver.local inherits basenode {  
    include mysql::server`         # BOOM, fails here because of Service["mysql"] redefinition             
}
0

4 Answers 4

3

Whenever you find yourself saying "I want this component to behave one way in one node, but a different way in another node", you probably need to be looking at a definition driven by variables, either via conditionals in the class definition, or substitutions in a template.

Unfortunately, your example shows you attempting to use inheritance, which is pretty badly broken in puppet and will cause you a lot of grief here because one of the things that it breaks is variable redefinition in nodes.

UPDATE: old answer details are obsolete

What I'm doing as of 2.7 is to ensure that all variables used by a class are not global variables at all, but rather parameters with reasonable defaults set. You still don't want to use the inheritance built into Puppet, but you can now override particular variables directly from the node definition, and you can even have a primitive form of inheritance manually by having one class take variables and then pass them to an invocation of another class. For example, you would have in your node definitions:


node experimental_server {
  # This will still call class 'databases', but will install Postgresql and not Mysql
  # as a default.  You can still override it the same way as with 'databases'
  class { 'mydept::nextgeneration': },
}
node server_with_mysql {
  class { 'databases':
    mysql_enabled => true,
  }
}
node server_with_no_db {
  class { 'databases': # this installs only the clients }
}

class databases (
  # By default, install no servers, just clients
  $pgsql_enabled => false,
  $mysql_enabled => false
)
{
  ...
}

class mydept::nextgeneration (
  # If not explicitly overridden, an undef value passed as a parameter to a class
  # assumes the default value in that class
  $mysql_enabled => undef,
  $pgsql_enabled => true
)
{
  class { 'databases':
    mysql_enabled => $mysql_enabled,
    pgsql_enabled => $pgsql_enabled,
  }
}

As a commenter noted, there is now also a nifty new feature called Hiera, which allows you to offload defaults into a separate data structure, and you should look into it if you have Puppet 3.2 available. Unfortunately, many Linux distributions are still shipping with 2.6 or 2.7, so this may not yet be an option for you.

Previous answer details preserved below

The way that I generally handle something like this is to create a 'default_parameters.pp' file that has nothing in it but default assignments for a long list of variables, so in this case, it would contain a line something like $mysql_enabled = false. This file is then included by the module's init.pp file. Then, the node definitions file would look something like:

import "mymodule"

node myserver.local {   # NOTE: no inheritance!
  $mysql_enabled = true
  include mysql::server
}

node otherserver.local {
  include mysql::server
}

Then in the class definition for mysql::server you check if $mysql_enabled is true, and if so, use a selector on enable and ensure.

This breaks instantly if node myserver.local inherits anything, because that will lock the value of the variables at inherit time, and further changes inside the node definition will have no effect, but otherwise I use this technique all over the place to have individual components behave differently without generating multiple classes.

If you absolutely have to have inheritance, but the list of machines with mysql enabled is small, you can take an alternate route: instead of setting a variable in the node definition, build your selector off of $fqdn, with the default to disable, and selected fqdns enabled.

2
  • interesting, i have to try this. Currently, i have a whole scheme with variables and an exec in a define (this works), which i partially described on the puppet mailing list. This was a long discussion, but in the end, no one could find a way to do it
    – Alex F
    Mar 3, 2010 at 16:13
  • Unfortunately this method is now deprecated, as dynamic scope variables are being removed in a future version of Puppet -- see docs.puppetlabs.com/guides/scope_and_puppet.html An alternative method is to use variables defined by hiera on a per-node basis (and with a default set in a common data source). This does this all much more cleanly now :) Oct 3, 2013 at 18:33
2

Have a go with this:

# In stripdown.pp : 
class stripdown {
    service { "mysql": 
         enable => "false", 
         ensure => "stopped" 
    }
}

# In mysql.pp : 
class mysql::server {  

    if defined(Service["mysql"]) {
        Service["mysql"] {  
            enable     => true,  
            ensure     => running,  
            hasrestart => true,  
            hasstatus  => true,  
            path       => "/etc/init.d/mysql",  
            require    => Package["mysql-server"],  
        }
    } else {
        service { "mysql":  
            enable     => true,  
            ensure     => running,  
            hasrestart => true,  
            hasstatus  => true,  
            path       => "/etc/init.d/mysql",  
            require    => Package["mysql-server"],  
        }
    }
}

# Then nodes in nodes.pp : 
node basenode {
    include stripdown 
}

node myserver.local inherits basenode {  
    include mysql::server
}

This of course comes with the caveat that you already have Package["mysql-server"] defined elsewhere was it will fail as written without it due to your require statements.

The other error I found was that you had too many spaces after the options. There options in the stanza must be aligned no more than 1 space greater than the longest option name.

0

Give this a go

# In stripdown.pp : 
class stripdown {
    service { "mysql": 
         enable => "false", 
         ensure => "stopped" 
    }
}

# In mysql.pp : 
class mysql::server inherits stripdown {  
    Service["mysqld"] {  
        enable      => true,  
        ensure      => running,  
        hasrestart  => true,  
        hasstatus   => true,  
        path        => "/etc/init.d/mysql",  
        require     => Package["mysql-server"],  
    } 
}

# Then nodes in nodes.pp : 
node basenode {
    include stripdown 
}

node myserver.local inherits basenode {  
    include mysql::server
}
1
  • Yes, i know about inheritance, but i do not want it in this case because the class mysql::server does not have relationship with the stripdown class. And moreover the mysql::server already inherits the class mysql I did find a way by using variables and execs, but it seems i run into strange puppet behavior, as it seems global variables of a class are set even if the class is not included by the node
    – Alex F
    Dec 11, 2009 at 14:09
0

One thing you may want to look at is Virtual Ressources to get around the multiple definition of ressources. Documentation on that topic can be found : http://reductivelabs.com/trac/puppet/wiki/VirtualResources

Of course, the other way would be to simple create node objects for each host that does not run mysql and then load the stripped class there.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .