Search code examples
rubypuppet

puppet applly and deferred functions: Error: Unknown function 'mymodule::myupcase'


I'm following this puppet doc to test Deferred functions, below I replicate the steps.

cd ~/dev/src/local/puppet_tests
pdk new module mymodule
cd mymodule
pdk new class mymodule
mkdir -p lib/puppet/functions/mymodule
  • Next, you should paste the code below in your manifest/init.pp file:
class mymodule {
  $d = Deferred("mymodule::myupcase", ["mysecret"])

  notify { example :
    message => $d
  }
}

class { 'mymodule': }
  • Next, create a file name myupcase.rb under the directory lib/puppet/functions/mymodule and paste the code below:
Puppet::Functions.create_function(:'mymodule::myupcase') do
  dispatch :up do
    param 'String', :some_string
  end

  def up(some_string)
    Puppet::Pops::Types::PSensitiveType::Sensitive.new(some_string.upcase)
  end
end
  • Now, you should run the puppet apply manifest/init.pp
~/dev/src/local/puppet_tests/mymodule main*
❯ puppet apply manifests/init.pp
Notice: Compiled catalog for localhost.localdomain in environment production in 0.04 seconds
Error: Unknown function 'mymodule::myupcase'

Removing the following part of the code in the init.pp file, it works, but just because you remove the reference to the $d variable:

  notify { example :
    message => $d,
  }

What am I missing here ?

My S.O. is MacOX 13.4.1, puppet 8.1.0. Installed both puppet-agent and pdk using brew:

brew install --cask puppetlabs/puppet/puppet-agent
brew install --cask puppetlabs/puppet/pdk

EDITED:

I have made an additional test, now using the pdk new function to create the function, but the error persists:

cd ~/dev/src/local/puppet_tests
pdk new module mymodule
cd mymodule
pdk new class mymodule
pdk new function myupcase --type=v4

It have created some additional directory named spec/ with some additional files. The final myupcase.rb is:

# frozen_string_literal: true

# https://github.com/puppetlabs/puppet-specifications/blob/master/language/func-api.md#the-4x-api
Puppet::Functions.create_function(:"mymodule::myupcase") do
  dispatch :myupcase do
    param 'String', :a
    return_type 'String'
  end
  # the function below is called by puppet and and must match
  # the name of the puppet function above. You can set your
  # required parameters below and puppet will enforce these
  # so change x to suit your needs although only one parameter is required
  # as defined in the dispatch method.
  def myupcase(x)
    x.upcase
  end

  # you can define other helper methods in this code block as well
end

The init.pp file remains the same.


Solution

  • The function itself looks fine, and you appear to have placed it properly in your module. That puppet apply does not find it likely arises from your module not being in its module path (and you have already confirmed this). Similar errors would arise if the main class tried to use any other contents of the module: other classes, defined types, module data, etc.

    Overall, the last step of the documentation, wherein you test the function by applying manifests/init.pp seems very ill advised, and when it does work to test the function, that's only because the module's init.pp contains class { 'mymodule': } at top scope, which is a whole other level of bad.

    This is what you should do instead:

    • remove the class declaration class { 'mymodule': } from your module's init.pp. It simply should not be there.

    • ensure that the parent directory (~/dev/src/local/puppet_tests) of your module directory is in Puppet's modulepath.

    • if you want to test with puppet apply then create a separate manifest for that, anywhere:

      test.pp

      include mymodule
      

      Note: as a general rule, prefer include-like class declarations such as that over resource-like ones such as we removed from init.pp.

      Use the the command /path/to/puppet apply path/to/test.pp to run the test.