Stop People Calling Private Puppet Classes

When writing puppet modules sometimes you need to ensure that certain classes are only used within your module itself. For example a class that implements functionality based on the local operating system that should only be used by your public class. While reading though the new puppetlabs-mcollective modules source I came across a new pattern I’d not seen used in puppet before that achieves this in a very elegant way and i thought it was worth a second look.

First we create our module and two classes, the publicly usable one and the internal one we want to restrict access to -

$ mkdir -p modules/internal/manifests

# add the externally usable module
$ cat << 'EOC' > modules/internal/manifests/init.pp
class internal {

  notify { 'Inside internal': }

  include internal::private

}
EOC

Now we add the private class and the access checking code -

$ cat << 'EOC' > modules/internal/manifests/private.pp
class internal::private {

  if $caller_module_name != $module_name {
    fail("Use of private class ${name} by ${caller_module_name}")
  }

  notify { "inside ${module_name}": }

}
EOC

We can include the public class and everything works -

$ puppet apply --modulepath modules -e 'include internal' -v

Notice: Compiled catalog for 123 in environment production in 0.06 seconds
Info: Applying configuration version 'XXX'
Notice: Inside internal
Notice: /Stage[main]/Internal/Notify[Inside internal]/message: defined 'message' as 'Inside internal'
Notice: inside internal
Notice: /Stage[main]/Internal::Private/Notify[inside internal]/message: defined 'message' as 'inside internal'
Notice: Finished catalog run in 0.23 seconds

Now we create a second module called ‘external’ and try to include the private class from the ‘internal’ module.

$ mkdir -p modules/external/manifests

$ cat << 'EOC' > modules/external/manifests/init.pp
class external {

  notify { 'Hello from external': }

  include internal::private

}
EOC

$ puppet apply --modulepath modules -e 'include external' -v

Error: Use of private class internal::private by external at /home/.../modules/internal/manifests/private.pp:4 on node 123
Error: Use of private class internal::private by external at /home/.../modules/internal/manifests/private.pp:4 on node 123

As you can see, this fails. The $caller_module_name and $module_name variables are set in the local scope during compilation and refer to the module in which the specific instance of the surrounding defined type was declared and the name of the module that contains the current class respectively. There’s also the official description.

I think this is an elegant way to restrict which classes in your modules are public and it’s something I’ll be sprinkling over my own modules in the future. You can download the example modules if you want to try them yourselves or go to the original module puppetlabs-mcollective on GitHub.