When you work in a complex environment with more than a dozen developers, enforcing standards is not that easy. Of course, all modern editors (like VSCode) have plugins available doing linting on Java, Yaml, Puppet, etc.

Unfortunately, it is hard to check the actual usage of those plugins.

That’s why we thought of a way to do a check on submitted source code.

The following requirements were set. We want to:

  • puppet validate (delivered with puppet agent software)
  • puppet-lint (installed with gem)
  • yamllint (installed with pip3)
  • use a docker container with a minimal setup

We, therefore, created a docker image using the Dockerfile below and put the image in a (local) registry:

# Dockerfile for validation image
# Does code linting and validation

FROM alpine:3.9
ENV PUPPET_AGENT_VERSION="5.3.2"
RUN \
    apk update \
 && apk add --no-cache openssh-client \
                       git \
                       ruby \
                       ruby-rdoc \
                       ruby-json \
                       ruby-etc \
                       shadow \
                       less \
                       bash \
                       python3 \
                       py3-pip \
 && gem install puppet:${PUPPET_AGENT_VERSION} --no-rdoc --no-ri \
                puppet-lint --no-rdoc --no-ri \
                r10k --no-rdoc --no-ri \
 && pip3 install yamllint \
 && grep ns_users /etc/group || groupadd ns_users

COPY root /root/

WORKDIR /root/.ssh/
RUN ssh-keyscan -p 7999 ns-bitbucket.dev.infosupport.net >> known_hosts \
 && chmod 500 /root/.ssh \
 && chmod 400 *

WORKDIR /etc/puppetlabs/code/environments/

The root folder contains some ssh info and the linting check files for Puppet (.puppet-lint.rc) and Yaml (configuration.yaml) files. See examples of those files below.

.puppet-lint.rc

--no-autoloader_layout-check
--no-variable_is_lowercase-check

configuration.yaml

extends: default

rules:
  braces:
    level: warning
    max-spaces-inside: 1
  brackets:
    level: warning
    max-spaces-inside: 1
  # For readablity, multiple spaces can exist after a colon.
  colons: disable
    # For readabilaty
  commas:
    level: warning
  comments: disable
  comments-indentation: disable
  document-start: disable
  empty-lines:
    level: warning
  hyphens:
    level: warning
  indentation:
    level: warning
    indent-sequences: consistent
  line-length:
    max: 120
    level: warning
    allow-non-breakable-inline-mappings: true
  truthy: disable

Now we are ready with the setup. The next leap is to implement activation of the linting ‘microservice’.

From Bitbucket it is possible to create webhooks. When configured correctly, these webhooks can react to the creation of pull-requests.

When a pull request is created, this action results in a signal towards Jenkins. Jenkins has rich functionality to show such requests, but needs the Bitbucket branch source plugin to do so.

When configured as is shown in the documentation of the plugin, the result of a pull request creation is:

This is the moment the real action happens. The Bitbucket repository contains a Jenkins file, which is configured as shown in the beforehand picture. As you see, the creation of the pull request triggers the execution of a Jenkins file.

Jenkins starts periodically (about every minute) and starts executing what is recorded in the Jenkinsfile. The result in our case is execution of the puppet validation and linting followed by yaml linting.

In our case, the setup of Jenkinsfile for validation is:

1) pull validation image and start container
2) check changed files from pull request
2a) validate puppet file
2b) puppet lint
2c) yaml lint
3) publish results to Bitbucket

In the end, the creation of a pull request will – in case of errors – result in a comment on that pull request. How cool is that! And this is only one of the possible implementations. The sky is the limit.