Often codebases that grow organically end up with snippets of cut-and-pasted repeated code. Some parts of the repetition will be identical, and some parts will have subtle differences. Since this eventually becomes an unmaintainable mess, we must proactively refactor once we start down this path.
Take this sample class below and refactor it to reduce the spaghetti creep. Refactoring commonalities into named variables provides context and makes it easier to update and/or refactor later.
class system::admins {
require mysql::server
mysql_user { 'zack@localhost':
ensure => present,
max_queries_per_hour => 1200,
}
mysql_user { 'monica@localhost':
ensure => present,
max_queries_per_hour => 600,
}
mysql_user { 'ralph@localhost':
ensure => absent,
}
mysql_user { 'brad@localhost':
ensure => present,
max_queries_per_hour => 600,
}
mysql_user { 'luke@localhost':
ensure => present,
max_queries_per_hour => 600,
}
user { ['zack', 'monica', 'ralph', 'brad', 'luke']:
ensure => present,
}
}
Let's use Puppet 6 iteration. What commonalities can you see in this code? What differences do you see? Have you spotted the bug?
-
Change directory to your
[modulepath]/system
cd $(puppet agent --configprint environmentpath)/production/modules/system
-
Make a new class
admins
.pdk new class admins
-
Make a list of all admin users that exist.
- Make a list of all users who have had their accounts retired.
- Identify the parameter(s) common to most users and the outlier(s).
- Correct any errors discovered.
- Turn each list into the appropriate data structure.
- You may include parameters for each user, or rely on resource defaults.
- Edit
manifests/admins.pp
- Write the correct lambda block to manage resources for each user.
- Manage a
mysql_user
resource. - Manage a
user
resource.
- Manage a
-
Install the
puppetlabs/mysql
module if needed.puppet module install puppetlabs/mysql
-
Validate and test your new class.
pdk validate
-
Test your new class.
puppet apply -e 'include system::admins'
-
Commit and push your changes.
The pick()
function can be used to select the first non-undefined value from a list of values. It could be useful when handling default values in your refactored class.
Example:
$default_udlc_stratum = 15
# $udlc_stratum has been set elsewhere and is either
# "undef" or a numeric value
class { 'ntp':
udlc_stratum => pick($udlc_stratum, $default_udlc_stratum),
}
Refactor your code with the pick()
function to remove even more duplication.
[root@training modules]# tree system/
system/
├── examples
│ └── admins.pp
└── manifests
└── admins.pp
└── admins_pick.pp # this file will only exist if you did the extra credit
class system::admins {
require mysql::server
# You can either use this resource default, or declare each parameter
# directly in the $admins hash
# Mysql_user {
# max_queries_per_hour => '600',
# }
$retired = [ 'ralph' ]
$admins = {
'brad' => { max_queries_per_hour => '600' },
'monica' => { max_queries_per_hour => '600' },
'luke' => { max_queries_per_hour => '600' },
'zack' => { max_queries_per_hour => '1200' },
}
$admins.each |$user, $params| {
mysql_user { "${user}@localhost":
ensure => present,
max_queries_per_hour => $params['max_queries_per_hour'],
}
user { $user:
ensure => present,
managehome => true,
}
}
$retired.each |$user| {
mysql_user { "${user}@localhost":
ensure => absent,
}
user { $user:
ensure => absent,
}
}
}
include system::admins
class system::admins_pick {
require mysql::server
$default_max_queries_per_hour = '600'
$retired = [ 'ralph' ]
$admins = {
'brad' => {},
'monica' => {},
'luke' => {},
'zack' => { max_queries_per_hour => '1200' },
}
$admins.each |$user, $params| {
mysql_user { "${user}@localhost":
ensure => present,
max_queries_per_hour => pick($params['max_queries_per_hour'],
$default_max_queries_per_hour),
}
user { $user:
ensure => present,
managehome => true,
}
}
$retired.each |$user| {
mysql_user { "${user}@localhost":
ensure => absent,
}
user { $user:
ensure => absent,
}
}
}
| Previous Lab | Next Lab |