Far too often we abuse the "exec" type, when what we really need is a simple provider that allows for more complex tests and exception handling. This talk will show how to change that enormous "exec" resource to a new type and provider, then return your manifests to a declarative config description.
3. How execs can fail
$ puppet module install puppetlabs-apt
$ vim apt/manifests/key.pp
...
exec { "apt::key ${upkey} absent":
command => "apt-key del '${upkey}'",
path => '/bin:/usr/bin',
onlyif => "apt-key list | grep '${upkey}'",
user => 'root',
group => 'root',
logoutput => 'on_failure',
}
4. Convert the exec to a type/provider
'user' type
ensure
name
comment
'useradd'
shell
home provider
exists?, create, destroy
comment, comment=
useradd <user>
shell, shell=
home, home=
usermod <user>
userdel <user>
5. Types: properties and parameters
● Properties are changeable, e.g. a user's
shell or a service's start-at-boot flag
● Parameters represent other required data, e.
g. hasrestart on service
● All data can be validated and munged
● Types can be "ensurable", if the object can:
○ exist and not exist
○ be created
○ be destroyed
6. Convert the exec to a type/provider
'apt_key' type
ensure
key 'keyring' provider
key_server exists?
create apt-key list
destroy
apt-key --recv-keys..
apt-key del
7. Types: simple example
$ cat apt_key/lib/puppet/type/apt_key.rb
Puppet::Type.newtype(:apt_key) do
@doc = "Manages apt keys"
ensurable
newparam(:key) do
desc "The key ID"
isnamevar
end
newparam(:key_server) do
desc "Key server to download key form"
defaultto "pgp.mit.edu"
end
end
8. Types: known values
Puppet::Type.type(:file).newparam(:checksum) do
desc "The checksum type to use when
determining whether to replace a file's conten
ts.
The default checksum type is md5."
newvalues "md5", "md5lite", "mtime", "ctime",
"none"
defaultto :md5
end
9. Types: validation
module Puppet
newtype(:schedule) do
newparam(:repeat) do
desc "How often a given resource may be applied
in this schedule's `period`. Defaults to 1; must be an
integer."
validate do |value|
unless value.is_a?(Integer) or value =~
/^d+$/
raise Puppet::Error,
"Repeat must be a number"
end
end
10. Providers: getters/setters, ensurable
● getters and setters are implemented for each
property
● ensurable types also have exists?, create
and destroy to manage its existence
● list of commands that are required to run
● confined to certain operating systems via
facts
11. Providers: simple example
$ cat apt_key/lib/puppet/provider/apt_key/keyring.rb
Puppet::Type.type(:apt_key).provide(:keyring) do
commands :aptkey => "/usr/bin/apt-key"
def exists?
aptkey("list").include? resource[:key].upcase
end
def create
aptkey "adv", "--keyserver", resource[:key_server], "--recv-
keys", resource[:key].upcase
end
def destroy
aptkey "del", resource[:key].upcase
end
end
12. Providers: confinement
Puppet::Type.type(:group).provide :aix do
desc "Group management for AIX."
confine :operatingsystem => :aix
defaultfor :operatingsystem => :aix
Puppet::Type.type(:exec).provide :posix do
confine :feature => :posix
defaultfor :feature => :posix