A calmer approach

A few weeks ago I wrote an article which was more of a rant than necessary. I was trying to drastically alter the way we right one of our puppet modules, and not being a simple module like ntp it required a bit of flexibility. To give you an understanding of what our puppet module does, we deploy Alfresco, including the automation of upgrades and configuring varios aspects of the application including bespoke overrides here and there and all sorts of wizzardy in the middle. The original puppet module was written by Ken Barber and then re-written by Adrian and myself. Needless to say there was some big puppet style brains working on it and the time had come to make some fundamental changes, mainly to make it easier to deploy directly from built code. So a lot of the work I’ve been doing on the module meant that it was more important to have the module deploy code form the build servers as well as automatically update itself.

Well, I achieved that within the old module, but quite frankly, the module was about 10 times larger than it needed to be specifically for an operational deployment into the cloud. With this in mind I thought it would eb a good use of time to re-write it and make easier to maintain and hopefully easier to extend, hence the rant around inheritance; in my head I had the perfect solution, which due to some bugs didn’t work. Well Time has moved on and as always progress must continue.

Puppet modules, the easy way.

Okay, so What did I do to overcome the lack of inheritance yet not have all of the duplication that was in the old modules. Simples! combine the two. I gave it some thought and I realsed that the best way out of the situation is to make it so that the variables were set from one place, params, so even though there is some duplication, you still only have to set the variables in one place. As a result I wrote a simple puppet module which has not been tested…. as a demonstration, this is the same structure as a live module so in theory will work the same way.

Manifests

[matthew@rincewind manifests]$ ll
total 20
-rw-r--r--. 1 root root 1981 Feb 17 21:26 config.pp
-rw-r--r--. 1 root root 1757 Feb 17 21:06 init.pp
-rw-r--r--. 1 root root 1578 Feb 17 21:12 install.pp
-rw-r--r--. 1 root root  921 Feb 17 21:51 params.pp
-rw-r--r--. 1 root root 2480 Feb 17 21:57 soimaapp.pp

Let’s start at the top,

init.pp

# = Class: soimasysadmin
#
#   This is the init class for the soimasysadmin module, it loads other required clases
#
# == Parameters:
#
#   *application_container_class*             = Puppet application container class deploying soimasysadmin i.e. tomcat
#   *application_container_cache*             = Location of the tomcat cache directory
#   *application_container_home*              = Application container home directory i.e. /var/lib/tomcat6/
#   *application_container_user*              = User for application container i.e. tomcat
#   *application_container_group*             = Group for application container i.e. tomcat
#
# == Actions:
#   Include any classes that are needed i.e. params, install etc
#
# == Requires:
#
# == Sample Usage:
#
#   class	{ 
#			"soimasysadmin":
#				application_container_class		=> 	"tomcat6",
#				application_container_home		=>	"/srv/tomcat"
#		}
#
class soimasysadmin  ( 	$application_container_class     =	$soimasysadmin::params::application_container_class,
			                  $application_container_cache     =	$soimasysadmin::params::application_container_cache,
                  			$application_container_home      =	$soimasysadmin::params::application_container_home,
                  			$application_container_user      =	$soimasysadmin::params::application_container_user,
                  			$application_container_group     =	$soimasysadmin::params::application_container_group,
		                ) 	inherits soimasysadmin::params {

include soimasysadmin::install

# The application_container variables are set here, but the defaults are all in params, which is available thanks to inheritance, Because this is the top of the tree, these variables can be accessed by other classes as needed

}

install.pp

# = Class: soimasysadmin::install
#
#   This class will install and configure directories and system wide settings that are relavent to soimasysadmin
#
# == Parameters:
#
#   All Parameters should have defaults set in params
#
#   *logrotate_days*      = Number of days to keep soimasysadmin logs for
#
# == Actions:
#   - Set up the application_container
#   - Set up soimasysadmin directories as needed
#
# == Requires:
#   - ${::soimasysadmin::application_container_class},
#
# == Sample Usage:
#
#   class	{ 
#			"soimasysadmin":
#				application_container_class		=> 	"tomcat6",
#				application_container_home		=>	"/srv/tomcat"
#		}
#
#

class soimasysadmin::install 	(	$logrotate_days = $soimasysadmin::params::logrotate_days,
                        			) inherits soimasysadmin::params	{

  File  {
    require =>  Class["${::soimasysadmin::application_container_class}"],
    owner   =>  "${::soimasysadmin::application_container_user}",
    group   =>  "${::soimasysadmin::application_container_group}"
  }

  #
  # Create folders
  #
  define shared_classpath ( $application_container_conf_dir = "$::soimasysadmin::application_container_conf_dir") {

    file {
      "${::soimasysadmin::application_container_home}/${name}/shared":
      ensure  =>  directory,
      notify  =>  undef,
    }
    file {
      "${::soimasysadmin::application_container_home}/${name}/shared/classes":
      ensure  =>  directory,
      notify  =>  undef,
    }
	}

	file {
		"/etc/logrotate.d/soimasysadmin":
		source		=>	"pupet:///modules/soimasysadmin/soimasysadmin.logrotate",
	}

}

params.pp

# = Class: soimasysadmin::params
#
# This class sets the defaults for soimasysadmin parameters
#
# == Parameters:
#
#
# == Actions:
#   Set parameters that are needed globally across the soimasysadmin class
#
# == Requires:
#
# == Sample Usage:
#
#   class soimasysadmin::foo inherits soimasysadmin:params ($foo  = $soimasysadmin::params::foo,
#                                                 					$bar  = $soimasysadmin::params::bar) {
#   }
#
#
class soimasysadmin::params  ( ) {

#
# Application Container details
#

  $application_container_class            = "tomcat"
  $application_container_cache            = "/var/cache/tomcat6"
  $application_container_home             = "/srv/tomcat"
  $application_container_user             = "tomcat"
  $application_container_group            = "tomcat"


#
#	soimapp config
#

	$dev_override_enabled   = "false"
  $custom_dev_signup_url	= "http://$hostname/soimaapp" 

}

Okay that’s all that’s needed to do the install, Notice that each class inherits params. Interestingly enough Adrian informs me this probably shouldn’t work and is possibly a bug, as params is being inherited multiple times. I think the only reason it works is that we have no resources in the params.pp if we add a file resource in ti will fail. but nonetheless, this works (for now)

config.pp

# = Class: soimasysadmin::config
#
#		This class configures the soimasysadmin repository
#
# == Parameters:
#
#		All Parameters should have defaults set in params
#
#		*db_name*												= The Database name 
#		*db_username*										= User to connect to the DB with
#		*db_password*										= Password for the user
#		*db_server*											= The DB server host 
#		*db_port*												= DB port i.e. 3306
#		*db_pool_min*										= Min DB connection pool
#		*db_pool_max*										= Max DB connection pool
#		*db_pool_initial*								= Initial DB connection pool size
#
# == Actions:
#   Install and configure soimasysadmin repository component into appropriate
#   application container
#
# == Requires:
#   - Class["${soimasysadmin::params::application_container_class}"],
#
# == Sample Usage:
# 
#   class	{ 
#			"soimasysadmin::config":
#				db_password 	=>	"Hdy^D7D6fvndsakj(*80",
#		}
#
#

class soimasysadmin::config	(	$db_name					=	$soimasysadmin::params::db_name,
															$db_username			=	$soimasysadmin::params::db_username,
															$db_password,
															$db_server				=	$soimasysadmin::params::db_server,
															$db_port					=	$soimasysadmin::params::db_port,
															$db_pool_min			=	$soimasysadmin::params::db_pool_min,
															$db_pool_max			=	$soimasysadmin::params::db_pool_max,
															$db_pool_initial	=	$soimasysadmin::params::db_pool_initial,
														)	inherits soimasysadmin::params	{
	File {
    owner   =>  "${::soimasysadmin::application_container_user}",
		group		=>	"${::soimasysadmin::application_container_group}",
		notify	=>	Service["${::soimasysadmin::soimasysadmin::application_container_service}"],
	}

	#
	# properties file
	#

	# If extra config set do concatinate
  file {
    "${::soimasysadmin::application_container_home}/${::soimasysadmin::application_container_instance}/properties":
    content	=> template("soimasysadmin/properties"),
    mode		=> 0640,
  }
}

NB In this instance I use config.pp as a way of setting up default config for the application, things common, for example the DB, where as application specific config is done separately, this obviously depends on your application…

soimaapp.pp

# = Class: soimasysadmin::soimaapp
#
# This class installs the soimaapp front end for the soimasysadmin repository
#
# == Parameters:
#
#		All Parameters should have defaults set in params
#
#		*dev_override_enabled*						= Enable dev configuration override
#		*custom_dev_signup_url*						=	Custom dev signup url
#		*application_container_instance*	= Application container instance i.e. tomcat1, tomcat2, tomcatN
#		*application_container_conf_dir*	= Application container conf directory i.e. /etc/tomcat6/conf/
#		*application_container_service*		= Service name of application container i.e. tomcat6
#
# == Actions:
#   Install and configure soimaapp into appropriate application container
#
# == Requires:
#		- Class["${soimasysadmin::params::soimasysadmin_application_container_class}",
#						"soimasysadmin::install"],
#
# == Sample Usage:
# 
#		include soimasysadmin::soimaapp
#
class soimasysadmin::soimaapp	(	$dev_override_enabled								$soimasysadmin::params::dev_override_enabled,
															$custom_dev_signup_url							=	$soimasysadmin::params::custom_dev_signup_url,
															$application_container_instance			= $soimasysadmin::params::application_container_instance,
															$application_container_conf_dir			= $soimasysadmin::params::application_container_conf_dir,
															$application_container_service			= $soimasysadmin::params::application_container_service
															) inherits  soimasysadmin::params  {

	# Set Default actions for soimasysadmin files
	File	{
		notify	=>	Class["${::soimasysadmin::application_container_class}"],
		owner		=>	"${::soimasysadmin::application_container_user}",
    group   =>  "${::soimasysadmin::application_container_group}",
	}


	#
	#	Install / Upgrade War
	#
	
	# Push war file to application container
  file {
    "${::soimasysadmin::application_container_home}/${application_container_instance}/webapps/soimaapp.war":
    source	=> "puppet:///modules/soimasysadmin/soimaapp.war"
    mode		=> "0644",
  }

	#
	#	Shared class created
	#
	soimasysadmin::install::shared_classpath {
		"${application_container_instance}":
			application_container_conf_dir	=>	"${application_container_conf_dir}"
	}
	
	if	( $dev_override_enabled == "true" ) {	
		file {
			"${::soimasysadmin::application_container_home}/${application_container_instance}/shared/classes/soimasysadmin/web-extension/soimaapp-config-custom.xml":
			content => template("soimasysadmin/soimaapp-config-custom.xml"),
		}
	}	
}

Okay that’s the basics, in the examples above, soimaapp does application specific config, any generic settings are in params.pp, any global settings are in init.pp; hopefully the rest is self explanatory.

I haven’t included any templates or files, you can work that out, you are smart after all.

Now the bad news

I will say the above does work, i’ve even tested it with inheriting soimaapp.pp and applying even more specific config over the top and all seems well, so what exactly is the bad news…

I tested all of this on the latest version of puppet 2.7.10 and then came across this, luckily 2.7.11 is out and available but I just haven’t tried it yet. let’s hope it works!

Category:
Linux, Puppet
Tags:
,

Join the conversation! 2 Comments

  1. […] Puppet inheritance, revisited – 40+ […]

    Reply
  2. […] just “include modulename”. You can read more about modules and how I typically use them Here It was rather experimental at the time but with only a few tweaks it is now in a production […]

    Reply

Don't be Shy, Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: