Copyright 2020 Tim Sharpe
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
So, I ran into a bit of a problem yesterday. I was working on a Puppet module for the Sensu monitoring framework, part of which involved writing a custom Puppet type to manage the configuration of service checks. Normally this is a trivial matter, however in this case, the checks must be configured on both the server and the client and must therefore be able to notify either the server process or the client process or both to restart after a configuration change.
Obviously, I couldn’t just do this:
1
2
3
4
5
6
sensu_check { 'mycheck':
notify => [
Service['sensu-client'],
Service['sensu-server'],
],
}
As if the host was only running the client process, this would result in an error during the Puppet runs.
One option would be to use the following mess:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(defined(Service['sensu-client']) && defined(Service['sensu-server'])) {
Sensu_check {
notify => [
Service['sensu-client'],
Service['sensu-server'],
],
}
} elsif defined(Service['sensu-client']) {
Sensu_check {
notify => Service['sensu-client'],
}
} else {
Sensu_check {
notify => Service['sensu-server'],
}
}
Not the most elegant solution in the world. Ideally what I needed was
something like the existing autorequire
functionality that creates require
relationships between your custom type and the named resources automatically
but only if the resources exist in the node catalogue.
Unfortunately, such functionality doesn’t exist (yet), so I had to go some
digging around in Puppet’s codebase to implement it. The first thing to do was
to establish a way to create arbitrary notify
relationships. This turned out
to be easily done:
1
2
3
4
5
6
7
8
9
Puppet::Type.newtype(:mytype) do
def initialize(*args)
super
self[:notify] = [
"Service[myservice]",
"Service[myotherservice]",
]
end
end
Now, all I needed to do was make it so that it only notified the resources that actually existed in the node’s manifest
1
2
3
4
5
6
7
8
9
Puppet::Type.newtype(:mytype) do
def initialize(*args)
super
self[:notify] = [
"Service[myservice]",
"Service[myotherservice]",
].select { |ref| catalog.resource(ref) }
end
end
The moral of this extremely drawn out story? Anything is possible in Puppet if you’re willing to get your hands dirty. And this should really be a built in feature *hint*.
Copyright 2020 Tim Sharpe
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.