Ernestas Poškus

Technical blog

"We must view with profound respect the infinite capacity of the human mind to resist the introduction of useful knowledge." - Thomas R. Lounsbury

| github | goodreads | linkedin | twitter |

ansible 2 / elasticsearch 2 / kernel 2 / leadership 1 / linux 2 / mnemonics 1 / nginx 1 / paper 40 / personal 5 / rust 1 / tools 2 /

Generating configuration from Ansible variables

WC 281 / RT 2min


If you have ever tried to render Ansible multi hash or list variable you probably something alike.

global:
  scrape_interval: "{{ prometheus_config_global_scrape_interval | to_nice_yaml }}"
  evaluation_interval: "{{ prometheus_config_global_evaluation_interval | to_nice_yaml }}"
  scrape_timeout: "{{ prometheus_config_global_scrape_timeout | to_nice_yaml }}"
  external_labels: "{{ prometheus_config_global_external_labels | to_nice_yaml }}"

This generates invalid and ugly YAML.

global:
  scrape_interval: "15s
...
"
  evaluation_interval: "30s
...
"
  scrape_timeout: "10s
...
"
  external_labels: "null
...
"

TLDR

If you are persistent thus configuration maniac you probably found a way either by destructuring hash or made extra redundant variables around complex one. But there is a better way that I came up with.

{{
{
'global': {
  'scrape_interval': prometheus_config_global_scrape_interval,
  'evaluation_interval': prometheus_config_global_evaluation_interval,
  'scrape_timeout': prometheus_config_global_scrape_timeout,
  'external_labels': prometheus_config_global_external_labels }
} | to_nice_yaml
}}

Here we are using Jinja 2 hash syntax and creating new hash with wanted keys inside of block later piping through to_nice_yaml filter as well. This generates pretty and valid YAML.

global:
    evaluation_interval: 30s
    external_labels: null
    scrape_interval: 15s
    scrape_timeout: 10s

Same applies to more complex variable definitions like this hash configuration inside of array/list.

prometheus_config_scrape_configs:
  - job_name: 'prometheus'
    honor_labels: true
    scrape_interval: '15s'
    scrape_timeout: '2s'
    metrics_path: '/metrics'
    scheme: 'http'
    static_configs:
      - targets:
          - "{{ prometheus_web__listen_address }}" # Prometheus itself
          - "{{ prometheus_alert_manager_web__listen_address }}" # Alert manager

  - job_name: 'consul-services'
    consul_sd_configs:
      - server: "consul.service.consul:8500"
        services:
          - nodeexporter

Variable used in template.

{% if prometheus_config_scrape_configs is not none and prometheus_config_scrape_configs | length > 0 %}
{{ {'scrape_configs': prometheus_config_scrape_configs} | to_nice_yaml }}
{% endif %}

End result here.

scrape_configs:
-   honor_labels: true
    job_name: prometheus
    metrics_path: /metrics
    scheme: http
    scrape_interval: 15s
    scrape_timeout: 2s
    static_configs:
    -   targets:
        - 192.168.250.11:9090
        - 192.168.250.11:9093
-   consul_sd_configs:
    -   server: consul.service.consul:8500
        services:
        - nodeexporter
    job_name: consul-services

Examples are taken from by ansible-prometheus playbook: https://github.com/ernestas-poskus/ansible-prometheus.