modules: Add prometheus and grafana modules

These are really stubs, and need some more testing and integration, but
there were some people who expressed interest in this, so let's push it
early.
This commit is contained in:
James Shubin
2025-02-03 04:46:44 -05:00
parent ab9c1d3d96
commit bdc46648ff
11 changed files with 2117 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
# To troubleshoot and get more log info enable ldap debug logging in grafana.ini
# [log]
# filters = ldap:debug
[[servers]]
# Ldap server host (specify multiple hosts space separated)
host = "127.0.0.1"
# Default port is 389 or 636 if use_ssl = true
port = 389
# Set to true if LDAP server should use an encrypted TLS connection (either with STARTTLS or LDAPS)
use_ssl = false
# If set to true, use LDAP with STARTTLS instead of LDAPS
start_tls = false
# The value of an accepted TLS cipher. By default, this value is empty. Example value: ["TLS_AES_256_GCM_SHA384"])
# For a complete list of supported ciphers and TLS versions, refer to: https://go.dev/src/crypto/tls/cipher_suites.go
tls_ciphers = []
# This is the minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.1, TLS1.2, TLS1.3.
min_tls_version = ""
# set to true if you want to skip ssl cert validation
ssl_skip_verify = false
# set to the path to your root CA certificate or leave unset to use system defaults
# root_ca_cert = "/path/to/certificate.crt"
# Authentication against LDAP servers requiring client certificates
# client_cert = "/path/to/client.crt"
# client_key = "/path/to/client.key"
# Search user bind dn
bind_dn = "cn=admin,dc=grafana,dc=org"
# Search user bind password
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
bind_password = 'grafana'
# We recommend using variable expansion for the bind_password, for more info https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
# bind_password = '$__env{LDAP_BIND_PASSWORD}'
# Timeout in seconds (applies to each host specified in the 'host' entry (space separated))
timeout = 10
# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"
search_filter = "(cn=%s)"
# An array of base dns to search through
search_base_dns = ["dc=grafana,dc=org"]
## For Posix or LDAP setups that does not support member_of attribute you can define the below settings
## Please check grafana LDAP docs for examples
# group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"
# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
# group_search_filter_user_attribute = "uid"
# Specify names of the ldap attributes your ldap uses
[servers.attributes]
name = "givenName"
surname = "sn"
username = "cn"
member_of = "memberOf"
email = "email"
# Map ldap groups to grafana org roles
[[servers.group_mappings]]
group_dn = "cn=admins,ou=groups,dc=grafana,dc=org"
org_role = "Admin"
# To make user an instance admin (Grafana Admin) uncomment line below
# grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used
# org_id = 1
[[servers.group_mappings]]
group_dn = "cn=editors,ou=groups,dc=grafana,dc=org"
org_role = "Editor"
[[servers.group_mappings]]
# If you want to match all (or no ldap groups) then you can use wildcard
group_dn = "*"
org_role = "Viewer"

View File

@@ -0,0 +1,29 @@
apiVersion: 1
#deleteDatasources:
# - name: "{{ .name }}"
# orgId: 1
# Mark provisioned data sources for deletion if they are no longer in a provisioning file.
# It takes no effect if data sources are already listed in the deleteDatasources section.
prune: true
datasources:
{{ if .comment -}}
#
# {{ .comment }}
#
{{ end }}
- name: "{{ .name }}"
type: prometheus
access: proxy
# Access mode - proxy (server in the UI) or direct (browser in the UI).
url: "{{ .url }}"
jsonData:
httpMethod: POST
manageAlerts: true
prometheusType: Prometheus
#prometheusVersion: 2.44.0
#cacheLevel: 'High'
#disableRecordingRules: false
#incrementalQueryOverlapWindow: 10m

85
modules/grafana/main.mcl Normal file
View File

@@ -0,0 +1,85 @@
import "deploy"
import "golang"
class server() {
pkg "grafana" { # on fedora
state => "installed",
}
file "/etc/grafana/" {
state => $const.res.file.state.exists,
#recurse => true,
#purge => true,
owner => "root",
group => "grafana",
mode => "u=rwx,g=rx,o=", # dir
}
file "/etc/grafana/ldap.toml" {
state => $const.res.file.state.exists,
content => deploy.readfile("/files/ldap.toml"), # XXX: eventually template
owner => "root",
group => "grafana",
mode => "u=rw,g=r,o=",
Notify => Svc["grafana-server"],
}
file "/etc/grafana/grafana.ini" {
state => $const.res.file.state.exists,
content => golang.template(deploy.readfile("/files/grafana.ini.tmpl")),
owner => "root",
group => "grafana",
mode => "u=rw,g=r,o=",
Notify => Svc["grafana-server"],
}
file "/etc/grafana/provisioning/" {
state => $const.res.file.state.exists,
#recurse => true,
#purge => true,
owner => "root",
group => "grafana",
mode => "u=rwx,g=rx,o=", # dir
}
svc "grafana-server" {
state => "running",
startup => "enabled",
}
}
class server:prometheus_base() {
file "/etc/grafana/provisioning/datasources/" {
state => $const.res.file.state.exists,
recurse => true,
purge => true,
owner => "root",
group => "grafana",
mode => "u=rwx,g=rx,o=", # dir
}
}
# XXX: if selinux is enabled, this wasn't connecting!
class server:prometheus($name, $st) {
include prometheus_base
$url = $st->url || "http://localhost:9090"
$comment = $st->comment || ""
$tmpl = struct{
name => "${name}",
url => "${url}",
comment => "${comment}",
}
file "/etc/grafana/provisioning/datasources/${name}.yaml" {
state => $const.res.file.state.exists,
content => golang.template(deploy.readfile("/files/prometheus.yaml.tmpl"), $tmpl),
owner => "root",
group => "grafana",
mode => "u=rw,g=r,o=",
}
}

View File

View File

@@ -0,0 +1,13 @@
# Connection string for the PostgreSQL database. You need to either connect as
# superuser, or create a user with enough rights, as described in
# /usr/share/doc/prometheus-postgres-exporter/README.Debian
# DATA_SOURCE_NAME='postgresql://login:password@hostname:port/'
# DATA_SOURCE_NAME='user=prometheus host=/run/postgresql dbname=postgres'
DATA_SOURCE_NAME='{{ .dsn }}'
# Set the command-line arguments to pass to the server.
# Due to shell escaping, to pass backslashes for regexes, you need to double
# them (\\d for \d). If running under systemd, you need to double them again
# (\\\\d to mean \d), and escape newlines too.
ARGS="{{ .args }}"

View File

@@ -0,0 +1,13 @@
/etc/systemd/system/prometheus-postgres-exporter@.service
[Unit]
Description=Prometheus exporter for PostgreSQL on port %i
Documentation=https://github.com/prometheus-community/postgres_exporter
[Service]
User=prometheus
EnvironmentFile=/etc/default/prometheus-postgres-exporter-%i
ExecStart=/usr/bin/prometheus-postgres-exporter $ARGS
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,4 @@
- job_name: {{ .name }}
# The prometheus-node-exporter running on {{ .name }}.
static_configs:
- targets: ['{{ .host }}:{{ .port }}']

View File

@@ -0,0 +1,38 @@
# Generated config for Prometheus.
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'example'
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets: ['localhost:9093']
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
scrape_timeout: 5s
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']

251
modules/prometheus/main.mcl Normal file
View File

@@ -0,0 +1,251 @@
import "deploy"
import "fmt"
import "golang"
import "golang/strings" as golang_strings
import "golang/strconv" as golang_strconv
import "local"
import "os"
import "strings"
class server() {
pkg "golang-github-prometheus" { # fedora
state => "installed",
Before => File["/etc/prometheus/"],
Before => Svc["prometheus"],
}
file "/etc/prometheus/" {
state => $const.res.file.state.exists,
recurse => true,
purge => true,
owner => "prometheus",
group => "root",
mode => "u=rwx,g=rx,o=", # dir
}
file "${vardir}prometheus.yml.header" {
state => $const.res.file.state.exists,
content => golang.template(deploy.readfile("/files/prometheus.yml.tmpl")),
owner => "root",
group => "root",
mode => "u=rw,go=",
}
file "/etc/prometheus/prometheus.yml" {
state => $const.res.file.state.exists,
fragments => [
"${vardir}prometheus.yml.header", # also pull this one file
"${vardir}jobs.d/", # pull from this dir
],
owner => "prometheus",
group => "root",
mode => "u=rw,g=r,o=",
Notify => Svc["prometheus"],
}
svc "prometheus" {
state => "running",
startup => "enabled",
}
# permissions are important for this dir, make sure they're correct
file "/var/lib/prometheus/" {
state => $const.res.file.state.exists,
owner => "prometheus",
group => "root",
mode => "u=rwx,g=rx,o=", # dir
}
$vardir = local.vardir("prometheus/")
# Add the default exporter.
include job("node", struct{
#host => "localhost",
#port => 9100,
})
}
class server:job_base() {
file "${vardir}jobs.d/" {
state => $const.res.file.state.exists,
recurse => true,
purge => true,
owner => "root",
group => "root",
mode => "u=rwx,go=", # dir
}
}
class server:job($name, $st) {
# XXX: document why this is named job_base instead of base:job_base or
# change the compiler to use the second version?
include job_base
$host = $st->host || "localhost"
$port = $st->port || 9100
$comment = $st->comment || ""
$tmpl = struct{
name => "${name}",
host => "${host}",
port => fmt.printf("%d", $port),
comment => "${comment}",
}
file "${vardir}jobs.d/${name}.job" {
state => $const.res.file.state.exists,
content => golang.template(deploy.readfile("/files/prometheus.yml.frag.tmpl"), $tmpl),
owner => "root",
group => "root",
mode => "u=rw,go=",
}
}
class exporter() {
if os.is_family_redhat() { # we only use fedora atm
pkg "node-exporter" { # fedora
state => "installed",
#Before => File["/etc/default/prometheus-node-exporter"],
Before => Svc["prometheus-node-exporter"],
}
}
if os.is_family_debian() {
pkg "prometheus-node-exporter" { # debian
state => "installed",
#Before => File["/etc/default/prometheus-node-exporter"],
Before => Svc["prometheus-node-exporter"],
}
}
# TODO: manage /etc/default/prometheus-node-exporter
svc "prometheus-node-exporter" {
state => "running",
startup => "enabled",
}
}
class postgresql_base() {
#if os.is_family_redhat() {
# pkg [
# "", # XXX: No obvious Fedora package available
# ] {
# state => "installed",
# }
#}
if os.is_family_debian() {
pkg [
"prometheus-postgres-exporter",
] {
state => "installed",
Before => Svc["prometheus-postgres-exporter"],
}
}
# systemd service template file to allow for multiple of these
file "/etc/systemd/system/prometheus-postgres-exporter@.service" {
state => $const.res.file.state.exists,
content => deploy.readfile("/files/prometheus-postgres-exporter.service.tmpl"),
owner => "root",
group => "root",
mode => "u=rw,go=r",
# Depend => Pkg["prometheus-postgres-exporter"],
# Before => Svc["prometheus-postgres-exporter"],
}
}
# We call this postgresql instead of postgres.
# XXX: you may not wish to have spaces in your passwords
class postgresql($config) {
include postgresql_base
$host = $config->host || "127.0.0.1"
$port = $config->port || 5432
$user = $config->user || "prometheus"
$pass = $config->pass || ""
$dbname = $config->dbname || ""
$sslmode = $config->sslmode || false
$listen_port = $config->listen_port || 9187
panic($port <= 0)
$h = if $host == "" {
""
} else {
"host=${host}"
}
$o = fmt.printf("port=%d", $port)
$u = if $user == "" {
""
} else {
"user=${user}"
}
$p = if $pass == "" {
""
} else {
"password=${pass}"
}
$d = if $dbname == "" {
""
} else {
"dbname=${dbname}"
}
$m = if $sslmode {
""
} else {
"sslmode=disable"
}
$l = [$h, $o, $u, $p, $d, $m,] # filter out empties first (IT'S REQUIRED)
$dsn = strings.join_nonempty($l, " ") # trailing spaces break this exporter
$str_port = golang_strconv.itoa($port)
$str_listen_port = golang_strconv.itoa($listen_port)
$args = "--web.listen-address=:${str_listen_port}"
$tmpl = struct{
# DATA_SOURCE_NAME='postgresql://login:password@hostname:port/' # untested?
# DATA_SOURCE_NAME='user=prometheus host=/run/postgresql dbname=postgres' # untested?
# DATA_SOURCE_NAME='host=127.0.0.1 port=5432 user=prometheus password=hunter2 dbname=postgres sslmode=disable' # works
port => $port,
dsn => $dsn,
args => $args,
}
file "/etc/default/prometheus-postgres-exporter-${str_port}" {
state => $const.res.file.state.exists,
content => golang.template(deploy.readfile("/files/prometheus-postgres-exporter.default.tmpl"), $tmpl),
owner => "prometheus",
group => "root",
mode => "ug=rw,o=",
Depend => Pkg["prometheus-postgres-exporter"],
Before => Svc["prometheus-postgres-exporter@${str_port}"],
}
svc "prometheus-postgres-exporter@${str_port}" {
state => "running",
startup => "enabled",
}
$vardir = local.vardir("prometheus/")
# there can only be one on each listen port!
# if more than one of these is set on the same machine we'd error
file "${vardir}postgresql-${str_listen_port}" {
state => $const.res.file.state.exists,
content => "${str_port}",
owner => "root",
group => "root",
mode => "u=rw,go=",
}
}

View File