How to configure parameterized classes with arrays in puppet

puppet

My goal is to define a class that takes, as input, an array of pairs. Since I'm not sure how to define an array of pairs, I'm just going to use a pair of arrays instead.

class student($username, $full_name){
  notify {"user":  message => "username: ${username}\n"}
  notify {"fullname":  message => "fullname: ${full_name}\n"}
  exec {'finger': command => "/usr/bin/finger ${username}", logoutput => true }
}

$users = ['elion', 'azee', 'root']
$names = ['El Lion', 'Avery Zee', "Rooty Root"]

class { 'student':
  username => $users,
  full_name => $names
}

Upon doing a 'puppet apply file.pp' I expect the output to iterate through the two arrays, printing out the notifications and fingering the students (that sounds horrible) in the order given by the arrays. I tried this and what ended up happening, is that puppet conflated the arrays into one string, so the $users value ended up being: elionazeeroot and the $names value ended up being El Lion Avery Zee Rooty Root.

Two questions:

  1. Is there a way to define a class to handle a group of data, like I'm trying to do here?
  2. I suspect that I'm not using the right construct to achieve my goal. What should I be doing instead?

Best Answer

The natural way to represent your data in a programming language would be a hash like this:

$users = {
  elion => 'El Lion',
  azee  => 'Avery Zee',
  root  => 'Rooty Root,
}

While doing that in Puppet is possible, I've found it's best to decide what a resource definition for a single entry would be and then structure the hash as something you could pass to create_resources(). In this case, that would mean something like this:

define student ($full_name) {
  notify { "user ${title}":
    message => "username: ${title}\n",
  }
  notify { "fullname ${title}":
    message => "fullname: ${full_name}\n",
  }
  exec { "finger ${title}":
    command   => "/usr/bin/finger ${title}",
    logoutput => true,
  }
}

$users = {
  elion => { full_name => 'El Lion'   },
  azee  => { full_name => 'Avery Zee' },
  root  => { full_name => 'Rooty Root },
}

create_resources(student, $users)

However, as Ger notes, you'll be best off if you use Hiera. Hiera lets you separate your code (the manifest definitions) from your data (in this case, the particular names of the students). In full, here's what I would do for your setup:

  • Put the student data in a hiera file. You can use JSON or YAML; I like YAML. It would be something like this:

    classroom::users:
      elion:
        full_name: El Lion
      azee:
        full_name: Avery Zee
      root:
        full_name: Rooty Root
    
  • Define a resource type for students and put it in classroom/manifests/student.pp in Puppet's $modulepath. It would look like the student resource type above, but would be named classroom::student.

  • Define a classroom class in classroom/manifests/init.pp with the following content:

    class classroom (
      $users = hiera('classroom::users', undef),
    ) {
      create_resources(classroom::student, $users)
    }
    

If you're using Puppet 3.x, the hiera() call in the class parameters is redundant, but you need it if you're still on Puppet 2.7.

Related Topic