8

I have a puppet module which deploys a JAR file and writes some properties files (by using ERB templates). Recently we added a "mode" feature to the application, meaning the application can run in different modes depending on the values entered in the manifest. My hierarchy is as follows:

setup

*config

**files

*install

Meaning setup calls the config class and the install class. The install class deploys the relevant RPM file according to the mode(s)

The config class checks the modes and for each mode calls the files class with the specific mode and directory parameters, the reason for this structure is that the value of the properties depends on the actual mode.

The technical problem is that if I have multiple modes in the manifest (which is my goal) I need to call the files class twice:

if grep($modesArray, $online_str) == [$online_str] {
    class { 'topology::files' :
      dir   => $code_dir,
      mode  => $online_str
    }
  }

  $offline_str = "offline"
  $offline_suffix = "_$offline_str"
  if grep($modesArray, $offline_str) == [$offline_str] {
    $dir = "$code_dir$offline_suffix"
    class { 'topology::files' :
      dir   => $dir,
      mode  => $offline_str
    }

However, in puppet you cannot declare the same class twice.

I am trying to figure out how I can call a class twice or perhaps a method which I can access its parameters from my ERB files, but I can't figure this out

The documentation says it's possible but doesn't say how (I checked here https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html#declaring-classes).

So to summarize is there a way to either:

  1. Call the same class more then once with different parameters
  2. (Some other way to) Create multiple files based on the same ERB file (with different parameters each time)
  • You might want to create custom resource: https://docs.puppetlabs.com/guides/custom_types.html – BroiSatse Aug 19 '14 at 11:05
  • How would I use it to solve my problem? – Sigal Shaharabani Aug 19 '14 at 11:19
  • Instead of creating a class (which you cannot use more then once), you create a custom resource, which does exactly same thing as your class and can be used multiple times. This is however pretty broad task so you will need to read the documentation and try to write it by yourself or you can wait for another answers, which might be more suitable. – BroiSatse Aug 19 '14 at 11:37
  • I'm now trying to create everything which does not involve the ERB in a function (which is also VERY hard to do), and then I'll have two different ERBs for the one file that requires the ERB. It is extremely difficult to write portable code in puppet – Sigal Shaharabani Aug 19 '14 at 11:59

2 Answers2

7

You can simply declare your class as a define:

define topology::files($dir,$mode){
  file{"${dir}/filename":
    content=> template("topology/${mode}.erb"), 
  }
}

That will apply a different template for each mode

And then, instantiate it as many times as you want:

if grep($modesArray, $online_str) == [$online_str] {
    topology::files{ "topology_files_${online_str}" :
      dir   => $code_dir,
      mode  => $online_str
    }
  }

  $offline_str = "offline"
  $offline_suffix = "_$offline_str"
  if grep($modesArray, $offline_str) == [$offline_str] {
    $dir = "$code_dir$offline_suffix"
    topology::files{ "topology_files_${online_str}" :
      dir   => $dir,
      mode  => $offline_str
    }
Raul Andres
  • 3,766
  • 15
  • 24
  • 1
    But bear in mind that the `$dir` must be different for each instance. Puppet cannot synchronize the same file to have two different contents at different stages of the transaction. There must be exactly one target state for Puppet to ensure. – Felix Frank Aug 19 '14 at 13:30
0

Your interpretation of the documentation is off the mark.

Classes in Puppet should be considered singletons. There is exactly one instance of each class. It is part of a node's manifest or it is not. The manifest can declare the class as often as it wants using the include keyword.

Beware of declaration using the resource like syntax.

class { 'classname': }

This can appear at most once in a manifest. Parameter values are now permanently bound to your class. Your node has chosen what specific shape the class should take for it.

Without seeing the code for your class, your question makes me believe that you are trying to use Puppet as a scripting engine. It is not. Puppet only allows you to model a target state. There are some powerful mechanics to implement complex workflows to achieve that state, but you cannot use it to run arbitrary transformations in an arbitrary order.

If you add the class code, we can try and give some advice on how to restructure it to make Puppet do what you need. I'm afraid that may not be possible, though. If it is indeed necessary to sync one or more resources to different states at different times (scripting engine? ;-) during the transaction, you should instead implement that whole workflow as an actual script and have Puppet run that through an exec resource whenever appropriate.

Felix Frank
  • 8,125
  • 1
  • 23
  • 30
  • Hi Felix, we have the exact same application which runs in two different modes. The only difference is that the input stream is different and the output table is different. I see no reason to duplicate my entire code when I can just use the same puppet scripts to deploy both modes (separately or both at once) – Sigal Shaharabani Aug 19 '14 at 14:46
  • Well, if both applications are indeed independent of one another, then Raul Andres' answer should basically do what you need. Your use case is then indeed not for classes, but for defined types instead. – Felix Frank Aug 19 '14 at 15:07
  • Thank you Felix. I am trying Raul's solution, it's still not working but I think I'm on the right path – Sigal Shaharabani Aug 19 '14 at 15:12
  • Sure. Do consider sharing the class code for better answers. – Felix Frank Aug 19 '14 at 15:16
  • The rest is just creating files and an ERB – Sigal Shaharabani Aug 19 '14 at 15:33