Skip to main content

Assigning resources to nodes with hiera in puppet

When I began using puppet, I quickly realised that configuration data was best kept separate from puppet manifests. Initially, I used extlookup and kept configuration data in CSV files. Then complex data structures came to puppet and I now use hirea/hiera-puppet with configuration data stored in hierarchical YAML files (other hiera backends are available). This article describes how to define in YAML the resources that should be applied to a node.

I have been using the hiera_include function for some time to define which classes are applied to each node. Usage is fairly simple. I have this fragment in nodes.pp:

node default {    hiera_include('classes')}

Then, in an hiera YAML file I have something like this:

classes:    - role::monitoring    - role::dns::master

At run time, the puppet classes role::monitoring and role::dns::master are applied to the node. Note, this could be in a node-specific config file, or something more generic that applied to several nodes.

This all works fine, but is limited to static classes. What if you have defined classes and you want to apply multiple instances of the same defined class on one node?

Let's suppose you have a puppet define that puts an apache website configuration on a node:

define apache::website::instance(    $ListenIP,    $ServerAlias = [],    $config = '') {# clever code goes here to instantiate the apache vhost}

You would use this define something like this:

apache::website::instance{'www.example.com':    ListenIP    => '1.2.3.4',    ServerAlias => ['example.com', 'foo.example.com'],    config      => 'DocumentRoot /var/www/sites/example<Directory /var/www/sites/example/>    AllowOverride None    Options +Indexes</Directory>'}

With the addition of a new custom puppet function it becomes possible to define the previous example in a YAML file.

Put the following content in $PUPPET_MODULE_DIR/custom/lib/puppet/parser/functions/hiera_resources.rb:

module Puppet::Parser::Functions  newfunction(:hiera_resources, :type => :statement) do |args|    raise Puppet::Error, "hiera_resources requires 1 argument; got #{args.length}" if args.length != 1    res_name = args[0][0]    apps = function_hiera_hash([res_name, {}])    apps.each { | res_type, res_params | function_create_resources([res_type, res_params]) }  endend

And add this to nodes.pp:

node default {    hiera_resources('resources')}

Now, to create the resource as in the previous example, add this to the hiera config:

resources:    apache::website::instance:        'www.example.com':            ListenIP: '1.2.3.4'            ServerAlias:                - 'example.com'                - 'foo.example.com'            config: |-                DocumentRoot /var/www/sites/example                <Directory /var/www/sites/example/>                    AllowOverride None                    Options +Indexes                </Directory>

You can now add resource to nodes dynamically by editing your hiera config!

Thanks to:

  • Volcane, for writing extlookup and hiera
  • Hunner, for helping with the custom function (I say "help"… he wrote it!)

Comments

Popular posts from this blog

Python logging with rich - writing to stderr - plain output when writing to file

Rich is a Python library for writing rich text (with color and style) to the terminal, and for displaying advanced content such as tables, markdown, and syntax highlighted code. Rich provides RichHandler , a logging handler for python's logging module which will format and colorize text written by the module. However, RichHandler writes to stdout by default. More specifically, it writes to a rich Console object which, by default, writes to stdout. To make RichHandler write to stderr by default, you must pass in a Console object which has been configured to write to stderr: import logging from rich.console import Console from rich.logging import RichHandler DATEFMT = "%Y-%m- %d T%H:%M:%SZ" FORMAT = " %(message)s " logging . basicConfig( level = "NOTSET" , format = FORMAT, datefmt = DATEFMT, handlers = [RichHandler(console = Console(stderr = True ))], ) logger = logging . getLogger(__name__) logger . i...

Fix python import order on save in vim with ruff and ale

My IDE of choice is vim. I use various tools to perform linting and code formatting, and configure them all with ALE  (the Asynchronous Lint Engine). After using several discrete tools ( black , isort , flake8 , etc) I have settled on using Ruff to do my python code formatting and linting. Here's the relevant fragment of my ALE config in my .vimrc: " ALE config let g :ale_fixers = { \ 'python' : [ 'ruff' , 'ruff_format' ], \} let g :ale_linters = { \ 'python' : [ 'ruff' ], \} let g :ale_python_ruff_use_global = 1 One of the last remaining wrinkles I had was getting Ruff to automatically sort import statements. Sorting imports is performed by the Ruff linter, not the formatter, which is documented here . The fix on the command line is to add an option, like this: ruff check --select I --fix The difficulty I had was getting this to happen in the editor when the file was saved. It turns out, all I needed to do was ...

Escaping special characters in wget username or password

I recently offered to help out with the hosting of a WordPress  site. It’s currently hosted somewhere with no shell access – just ftp – and there are a lot of images to transfer. I quickly figured out I could use wget to mirror the site, using something like: wget -m ftp://username:password@example.com However, this broke in this case because the username for the site contained an @ character (the username was user@example.com ). Turns out the solution was to encode the special chars using HTML notation. This is the command that did the trick: wget -m ftp://user%40example.com:password@example.com