[[email protected]:~]$

Setting up for Puppet Development


The last several months I’ve had the pleasure of diving into an extensive puppet project. This is a brief overview of my vim setup and some other tools I use. I am on Ubuntu currently, and this guide will be based around that.

Index

  1. Official Plugin
  2. Generic Options
  3. Puppet Language Server
  4. Errors
  5. Linting
  6. Puppet Debugger

Official Plugin

There is an official vim-plugin, however it is worth noting the syntax highlighting only supports version 4.x. If you happen to find yourself on an extremely old codebase, there is an older version which is no longer maintained. This can be found here.


Generic Options

These are the settings I use to edit .pp files/manifests. This will simply set your indent to two spaces when pressing tab. This will only work on files ending in .pp. This goes in your ~/.vimrc.

" Settings for puppet files
au BufNewFile,BufRead *.pp
  \ set tabstop=2 |
  \ set softtabstop=2 |
  \ set shiftwidth=2 |
  \ set textwidth=79 |
  \ set expandtab |
  \ set autoindent |
  \ set fileformat=unix

Puppet Language Server

When setting this up I initially followed this guide. There are some difference as I use vundle, and they don’t include steps for installing certain dependencys. They also dive into setting up solargraph. If that interests you, I definitely reccomend checking it out.

First, Install Node and add it to your path.

$ wget https://nodejs.org/dist/v14.17.6/node-v14.17.6-linux-x64.tar.xz -P ~/.local/share/
$ cd ~/.local/share/
$ tar -xvf node-v14.17.6-linux-x64.tar.xz
$ ln -s ~/.local/share/node-v14.17.6-linux-x64/bin/node ~/.local/bin/
$ ln -s ~/.local/share/node-v14.17.6-linux-x64/bin/npm ~/.local/bin/
$ ln -s ~/.local/share/node-v14.17.6-linux-x64/bin/npx ~/.local/bin/
$ node --version

Next Install vim-node-rpc

$ npm install --user vim-node-rpc 

I use Vundle, you are welcome to use your own plugin manager for vim. You’ll set Vundle to install the coc.vim plugin. Note, this uses the newest version of vim-puppet, if you need the older for a legacy codebase, you can swap it out. This will go at the top of ~/.vimrc

set nocompatible              " required
filetype off                  " required

" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

" alternatively, pass a path where Vundle should install plugins
"call vundle#begin('~/some/path/here')

" let Vundle manage Vundle, required
Plugin 'gmarik/Vundle.vim'
Plugin 'neoclide/coc.nvim'
Plugin 'mrk21/yaml-vim' " For hieradata
Plugin 'rodjek/vim-puppet' " supports version 4 - so be weary
Plugin 'vim-ruby/vim-ruby' "Facts, functions, etc...

" All of your Plugins must be added before the following line
call vundle#end()            " required
filetype plugin indent on    " required
" set status line for coc
set statusline+=%{coc#status()}

Reopen vim and type :PluginInstall. If it fails for dependencies for coc, run the following:

$ cd ~/.vim/bundle/coc.nvim/
$ npm install --user yarn
$ ~/.vim/bundle/coc.nvim/node_modules/yarn/bin/yarn install --frozen-lockfile

Then for the final time, Re-Open vim and type :PluginInstall once more.

You will also need to install the language server. Note that it does install a number of the puppet-lint gems.

$ git clone https://github.com/lingua-pupuli/puppet-editor-services.git
$ cd puppet-editor-services
$ bundle install
$ bundle exec rake gem_revendor

Lastly edit the Coc config $ vim +CocConfig - be certain to update the paths below to what reflects your system. It may take a while to initialize when you load into vim. It should state this at the bottom in the status bar.

{
  "languageserver": {
    "puppet": {
      "command": "ruby",
      "args": ["/path/to/puppet-editor-services/puppet-languageserver","--stdio","--puppet-settings=--moduledir,/path/to/module/path"],
      "cwd": "/path/to/puppet-editor-services",
      "filetypes": ["puppet"],
      "rootPatterns": [".git/", "metadata.json"]
    }
  }
}

While not part of the language server or official plugin, I do find SimplyFold very handy. I won’t be going over all of my plugins, but this one feels relevant. To install, add this to the vundle section of your ~/.vimrc - `Plugin ‘tmhedberg/SimplyFold’. Next add this after the install section. I have it bound to spacebar, but you can choose whatever case works best. It will fold long sections of code, which helps when working with larger manifests.

" Enable folding
set foldmethod=indent
set foldlevel=99

" Enable folding with the spacebar
nnoremap <space> za

Errors

If you get an error message such as the following:

Traceback (most recent call last):
        1: from ./puppet-languageserver:13:in `<main>'
/home/kchatland/github/puppet-editor-services/lib/puppet_languageserver.rb:218:in `init_puppet': uninitialized constant PuppetLanguageServer::Facter (NameError)

You are missing the facter gem, and the ruby script needs to be updated. Add require ‘facter’ to the top of the given file in the puppet-editor-services repo.

$ gem install facter --user
$ vim lib/puppet_languageserver.rb

require 'facter'

Puppet-Lint

A handy tool in correcting syntax or checking for code errors. If you don’t have ruby or ruby-gems, intall them first.

$ sudo apt install ruby ruby-gems

Next install the linter, these are a number of the more helpful lints. It is worth noting if you setup the server language plugin for vim it will install these as a dependency.

$ gem install --user puppet-lint \
  puppet-lint-strict_indent-check \
  puppet-lint-manifest_whitespace-check \
  puppet-lint-unquoted_string-check \
  puppet-lint-leading_zero-check \
  puppet-lint-absolute_classname-check \
  puppet-lint-trailing_comma-check \
  puppet-lint-file_ensure-check \
  puppet-lint-legacy_facts-check 

$ ln -s  ~/.gem/ruby/2.7.0/bin/puppet-lint ~/.local/bin/   

Here are a couple examples of some simple use cases

  • Calling the program will not make changes, it simply evaluates the file and displays a readable output with the errors it found.
    $ puppet-lint <file.pp>
    
  • This will auto-fix any issues it is able.
    $ puppet-lint --fix <file.pp>
    
  • This is the most common change I have seen puppet-lint fix that ends up breaking the agent. Since we are on an older version of puppet, we do still need to use the legacy facts syntax. If you don’t specify this flag, puppet-lint will adjust the syntax. The agent will subsequently fail when trying to read the catalogue.
    $ puppet-lint --no-legacy_facts-check --fix <file.pp>
    
  • Another helpful example, if you want to only adjust on specific aspect. There are a lot of manifests for instance, with incorrect whitespace spacing between arrows when declaring parameters for a class or resource. If you only wanted to fix those, you could do something like this:
    $ puppet-lint --fix --only-checks arrow_alignment <file.pp>
    

There are a lot more than the above. Here is a full list of all plugins for puppet-lint

 puppet-lint-absolute_classname-check puppet-lint-absolute_template_path 
 puppet-lint-alias-check puppet-lint-anchor-check puppet-lint-appends-check 
 puppet-lint-array_formatting-check puppet-lint-classes_and_types_beginning_with_digits-check
 puppet-lint-classes_and_types_beginning_with_digits-check puppet-lint-class_parameter-check 
 puppet-lint-concatenated_template_files-check puppet-lint-cuddled_else-check 
 puppet-lint-duplicate_class_parameters-check puppet-lint-ec2_facts-check 
 puppet-lint-empty_lines_around_body-check puppet-lint-empty_string-check 
 puppet-lint-empty_trailing_lines puppet-lint-exec_idempotent-check 
 puppet-lint-explicit_hiera_class_param_lookup-check puppet-lint-extended 
 puppet-lint-file_ensure-check puppet-lint-file_line_match-check 
 puppet-lint-fileserver-check puppet-lint-file_source_rights-check 
 puppet-lint-global_resource-check puppet-lint-halyard puppet-lint-i18n 
 puppet-lint-leading_zero-check puppet-lint-legacy_fact-check 
 puppet-lint-legacy_facts-check puppet-lint-manifest_whitespace-check 
 puppet-lint-metrics-check puppet-lint-no_chaining_arrows-check 
 puppet-lint-no_cron_resources-check puppet-lint-no_erb_template-check 
 puppet-lint-no_file_path_attribute-check puppet-lint-non_erb_template_filename-check 
 puppet-lint-no_symbolic_file_modes-check puppet-lint-numericvariable 
 puppet-lint-optional_default-check puppet-lint-package_ensure-check 
 puppet-lint-param-docs puppet-lint-parameter_type-check puppet-lint-param-types 
 puppet-lint-racism_terminology-check puppet-lint-recurse_file-check 
 puppet-lint-reference_on_declaration_outside_of_class-check 
 puppet-lint-resource_outside_class-check puppet-lint-resource_reference_syntax 
 puppet-lint-roles_and_profiles-check puppet-lint-roles-profiles 
 puppet-lint-security-plugins puppet-lint-space_after_comma-check 
 puppet-lint-spaceship_operator_without_tag-check puppet-lint-strict_indent-check 
 puppet-lint-template_file_extension-check puppet-lint-top_scope_facts-check 
 puppet-lint-topscope-variable-check puppet-lint-trailing_comma-check 
 puppet-lint-trailing_newline-check puppet-lint-uncuddled_else-check 
 puppet-lint-undef_in_function-check puppet-lint-unquoted_string-check 
 puppet-lint-usascii_format-check puppet-lint-use_ensure_packages-check 
 puppet-lint-variable_contains_upcase puppet-lint-version_comparison-check 
 puppet-lint-vim_modeline-check puppet-lint-wmf_styleguide-check 
 puppet-lint-world_writable_files-check puppet-lint-yumrepo_gpgcheck_enabled-check 

Puppet Debugger

A handy tool to debug puppet code in real time without having to go through the whole process of pushing a commit, pulling with r10k, and running the agent, or whatever your process may be. There are some limitations, but it can be handy.

The easiest method for me, was via a docker container. There are other install methdos if so desired.

$ docker run -ti nwops/puppet-debugger

If you need to install docker, consult their documentation. Note that if you recieve a socket permission error, you probably need to add yourself to the group. You can then either reboot, logout, or start a new shell session as your user

$ usermod -aG docker ${USER}
$ exec su -l $USER

Simply call puppet debugger to drop into the prompt. The debugger comes a wide variety of commands that allow you to inspect the catalog, your code or reference items currently in scope. As an example, here is me setting an array, then manipulating the items with a regular expression. I can see what the return values look like, and it catches basic syntax errors.

1:>> nodes = [ 'node1', 'node2'  ]
 => Illegal attempt to assign to 'a Name'. Not an assignable reference (line: 1, column: 1)
2:>> $nodes = [ 'node1', 'node2'  ]
 => [
  [0] "node1",
  [1] "node2"
]
3:>> $node_path = regsubst($nodes, '^(.*)$', "\1:/new/path")
 => [
  [0] "node1:/new/path",
  [1] "node2:/new/path"
]

If a clean slate is needed, simply run reset. The scope of the application far exceeds what should go here, if this sounds beneficial, I would recommend looking over their documentation. https://docs.puppet-debugger.com/getting_started/