Skip to main content

Checks

A check asks one question of your inventory and gives a verdict: "Does the edge router exist?", "Are all device names unique?", "Does every server have a hostname?". MiniPAM runs your checks after every inventory change and shows the result on one page.

Checks can also guard a template: when a guard fails, the template's new output is not published, and the last approved one keeps being served.

Open Templates & checks → Checks. Its Issues tab lists problems that break MiniPAM's built-in inventory rules, such as overlapping networks; editors see it. Custom checks and Template guards hold the checks you write.

Checks page with the summary line, the Custom checks and Template guards tabs, and two checks: an output check marked Not linked and a passing inventory check that guards two templates
Checks: the summary line, then your checks and the guards on your templates.

Read the page​

The summary line counts what is failing, what has warnings, what passes, and what has not run yet, and says whether inventory has changed since checks last ran. Custom checks lists every check, failing first, with the first failure and the templates it guards. Template guards lists every template with its guard status:

StatusMeaning
PassingAll requirements passed.
WarningsOnly warn lines failed. The output can still be published.
FailingA require line failed. For a template this shows as Blocked.
UnprotectedThe template has no guards at all. Add a require line or link a check.
Not runNo result yet.

A result marked Edited since last run or Inventory changed since last run may be out of date. Run all runs every inventory check again.

Write a check​

  1. Choose New check.
  2. Pick a starter: Required records exist, Values are unique, Required fields are present, or Blank inventory check.
  3. Edit the source. Test check (Ctrl+Enter / ⌘Enter) runs it against current inventory and shows each requirement's result.
  4. Save check. It runs straight away and appears in the list.
Check editor with the Edit & test, Used by, and Revision history tabs, Jinja source on the left, and a passed requirement under Check results on the right
Writing a check: source on the left, results on the right.

A check uses the same language as templates, plus two tags:

  • {% require condition, … %} must hold. If it fails, the check fails.
  • {% warn condition, … %} should hold. If it fails, the check shows a warning.

Every check needs at least one require. Give each one a code, a message that tells the next person what to fix, and optionally the expected and actual values:

{% set router = data.devices.filter('name:eq:edge-router') %}
{% require router.exists(),
code='edge_router_present',
message='The edge router must exist',
expected='one device named edge-router',
actual=router.count() %}

Several small, named requirements are easier to act on than one big condition.

Guard a template​

A template is guarded by require lines in its own source, by linked checks, or both. Open the template and its Checks tab:

  • In this template lists the template's own requirements.
  • Shared checks lists linked checks. Link existing adds one of your checks; New output check writes a check that inspects the generated file.
  • Run checks generates the file and runs every guard against it. Latest result lists each requirement's verdict and says whether it came from the template or a shared check.
A template's Checks tab with In this template, a linked shared check, and a Latest result where an inline requirement and the shared check both passed
A template's guards: its own requirements and the shared checks linked to it.

Output checks​

An output check reads the generated file through artifact: artifact.content, artifact.filename, artifact.media_type, and artifact.size. Create one from the template it guards with New output check. It is tested against that template's output, and Save & link check links it and takes you back.

{% require artifact.content|trim|length > 0,
code='output_not_empty',
message='The generated file must not be empty',
expected='non-empty output',
actual=artifact.size %}

{% require 'server_name' in artifact.content,
code='nginx_server_name_present',
message='The Nginx output must contain at least one server_name',
expected='server_name' %}

Shared checks​

A linked check always uses its latest saved version. When you edit a check that guards templates, MiniPAM first tests the change against each of them. If the change would make any fail, it names them and asks before saving. A check's Used by tab lists the templates it guards.

More examples​

Require named services, and that Grafana still has something providing it:

{% for name in ['DNS', 'Grafana'] %}
{% set service = data.services.filter('name:eq:' ~ name) %}
{% require service.exists(),
code='service_present',
message=name ~ ' must be recorded as a service',
expected='one service named ' ~ name,
actual=service.count() %}
{% endfor %}

{% set grafana = data.services.filter('name:eq:Grafana').first() %}
{% if grafana %}
{% set providers = data.applications.of(grafana) %}
{% require providers.exists(),
code='grafana_provider_present',
message='Grafana must have at least one application providing it',
expected='one or more applications',
actual=providers.count() %}
{% endif %}

Every device needs a unique name and a hostname:

{% require is_unique(data.devices, 'name'),
code='device_names_unique',
message='Device names must be unique',
actual=duplicate_values(data.devices, 'name') %}

{% warn all_present(data.devices, 'hostname'),
code='hostnames_complete',
message='Every device should have a hostname' %}

No address may sit outside the networks you manage:

{% for address in data.ip_addresses %}
{% require address.network,
code='address_in_network',
message=address.address ~ ' does not belong to any network',
actual=address.address %}
{% endfor %}

A check only reports. It never changes inventory. See the template language for everything you can use in a check.