feat: Add grafana role.

This commit is contained in:
Laur Ivan 2022-12-19 14:18:31 +01:00
commit 23a5ea6703
27 changed files with 643 additions and 0 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
github: equinoxel
patreon: laurivan

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.retry
*/__pycache__
*.pyc
.cache
.venv
.env.yml
docker-compose.yml

29
.travis.yml Normal file
View File

@ -0,0 +1,29 @@
---
language: python
python: "2.7"
# Use the new container infrastructure
sudo: false
# Install ansible
addons:
apt:
packages:
- python-pip
install:
# Install ansible
- pip install ansible
# Check ansible version
- ansible --version
# Create ansible.cfg with correct roles_path
- printf '[defaults]\nroles_path=../' >ansible.cfg
script:
# Basic role syntax check
- ansible-playbook tests/test.yml -i tests/inventory --syntax-check
notifications:
webhooks: https://galaxy.ansible.com/api/v1/notifications/

33
.yamllint Normal file
View File

@ -0,0 +1,33 @@
---
# Based on ansible-lint config
extends: default
rules:
braces:
max-spaces-inside: 1
level: error
brackets:
max-spaces-inside: 1
level: error
colons:
max-spaces-after: -1
level: error
commas:
max-spaces-after: -1
level: error
comments: disable
comments-indentation: disable
document-start: disable
empty-lines:
max: 3
level: error
hyphens:
level: error
indentation: disable
key-duplicates: enable
line-length: disable
new-line-at-end-of-file: disable
new-lines:
type: unix
trailing-spaces: disable
truthy: disable

0
CHANGELOG.md Normal file
View File

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2022 Laur Ivan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

191
README.md Normal file
View File

@ -0,0 +1,191 @@
# laurivan.grafana
This role installs Grafana via Docker.
## Requirements
None
## Role Variables
All variables are listed below (see also `defaults/main.yml`).
### Grafana Core Variables
Grafana requires a couple of secrets for data encryption:
```yml
grafana_secret_key: 'changeme'
grafana_utils_secret: 'changeme'
```
You also need to specify the deployment type. Usually it's `production`
```yml
grafana_deployment: ''
```
You also need to define how you access grafana:
- `grafana_port` is the port mapping in Docker. Grafana runs at port 3000, which is alos the default
- `grafana_url` is the public URL where we see Grafana. If you use reverse proxy mapping, put the URL of the reverse proxy (in my case *[this one](https://wiki.home.laurivan.com)*).
- `grafana_force_https` will run with HTTPS if true. you can define it as *false* If you're behind a proxy or you don't have a certificate. It defaults to `false`.
- `grafana_enable_updates` will enable updates if true. Please read [the documentation](https://app.getgrafana.com/s/770a97da-13e5-401e-9f8a-37949c19f97e/) for what this implies (e.g. telemetry)
- Define `grafana_cdn_url` if you have a CDN. Defaults to *empty*
**Note**: `grafana_url` will define the authentication redirect url for e.g. authentik
You can define which debug messages to be logged via `grafana_debug`.
### Storage
Following values are defined for the docker-compose:
```yml
grafana_volume_base: "/mnt/grafana"
grafana_setup_path: '{{ grafana_volume_base }}/config'
grafana_volume_redis: "{{ grafana_volume_base }}/redis"
grafana_volume_db: "{{ grafana_volume_base }}/db"
grafana_volume_s3: "{{ grafana_volume_base }}/s3"
```
Please note that `grafana_volume_db` and `grafana_volume_s3` are actually created only if local posstgres and fake_s3 containers are created by configuration below.
You can specify a logo too via `grafana_team_logo_url`. By default this is empty.
You can also change the default language via `grafana_language`. The role defaults the language to *en_US*.
### Authentication
Grafana authentication can happen via:
- OIDC
- Google authentication
- Slack
You need to define at least one of them.
#### OIDC
OIDC parameters are
```yml
oidc_client_id:
oidc_client_secret:
oidc_auth_uri:
oidc_token_uri:
oidc_userinfo_uri:
```
Your authentication app should provide you all the above. I use something along the lines:
```yml
oidc_client_id: "changeme"
oidc_client_secret: "changeme"
oidc_auth_uri: "https://sso.laurivan.com/application/o/authorize/"
oidc_token_uri: "https://sso.laurivan.com/application/o/token/"
oidc_userinfo_uri: "https://sso.laurivan.com/application/o/userinfo/"
oidc_username_claim: "preferred_username"
```
**Note**: you will probably need to provide the redirect URL to the authentication application. For Authentik, you can find it in the **Provider** for the specific application.
#### Google ID
You need to define:
```yml
grafana_google_client_id:
grafana_google_client_secret:
```
#### Slack
You need to define
```yml
grafana_slack_client_id:
grafana_slack_client_secret:
```
### Database
You need to assign a database to Grafana. This role allows you to launch Postgres in a container via:
```yml
grafana_db_schema: "postgres"
grafana_db_host: "postgres"
grafana_db_port: "5432"
grafana_db_user: "postgres"
grafana_db_password: "changeme"
grafana_db: "grafana"
```
If the db_host is not "postgres", then we assume the db is external and not spin up the docker container.
By default, PostgreSQL is not secured. If you have a secure database instance, set the `grafana_db_ssl` variable to "enable".
### S3
We define the following variables:
```yml
grafana_fake_s3: true
grafana_fake_s3_port: 4569
grafana_aws_access_key_id:
grafana_aws_secret_access_key:
grafana_aws_region:
grafana_aws_s3_upload_bucket_url: "http://s3:4569"
grafana_aws_s3_upload_bucket_name: grafana-bucket
grafana_aws_s3_upload_max_size: "26214400"
grafana_aws_s3_force_path_style: "true"
grafana_aws_s3_acl: "private"
```
You need S3 (or S3-like) storage for e.g. uploaded files. By default, the role spins up the fake S3 only if `fake_s3` variable is true.
I use [MinIO](https://min.io/) with something like:
```yml
grafana_fake_s3: ""
grafana_aws_access_key_id: "change me"
grafana_aws_secret_access_key: "change me"
grafana_aws_region: "my-rack"
grafana_aws_s3_upload_bucket_url: "http://minio,example.com:9000"
grafana_aws_s3_upload_max_size: "26214400"
grafana_aws_s3_force_path_style: "true"
grafana_aws_s3_acl: "private"
```
### Email
Grafana can send notification emails if you set up the SMTP variables:
```yml
grafana_smtp_host:
grafana_smtp_port:
grafana_smtp_username:
grafana_smtp_password:
grafana_smtp_from_email:
grafana_smtp_reply_email:
```
## Dependencies
You need a machine with docker and docker-compose installed.
## Example Playbook
```yml
- hosts: servers
roles:
- 'laurivan.grafana'
```
## License
MIT
## Author Information
This role was created in 2022 by [Laur Ivan](https://www.laurivan.com).

3
TODO.md Normal file
View File

@ -0,0 +1,3 @@
# TODOs
- [ ] Write a proper README.

21
defaults/main.yml Normal file
View File

@ -0,0 +1,21 @@
---
# defaults file for grafana
influxdb:
username: influxdb
password: change_me
org: example.com
bucket: my_bucket
admin_token: change_me
grafana:
username: changeme
password: changeme
grafana_volume_base: "/mnt/grafana"
grafana_setup_path: "{{ grafana_volume_base }}/config"
grafana_volume_grafana: "{{ grafana_volume_base }}/grafana"
grafana_volume_influxdb: "{{ grafana_volume_base }}/influxdb"
grafana_uid:
grafana_gid:

2
handlers/main.yml Normal file
View File

@ -0,0 +1,2 @@
---
# handlers file for grafana

34
meta/main.yml Normal file
View File

@ -0,0 +1,34 @@
galaxy_info:
author: Laur Ivan
namespace: laurivan
role_name: grafana
description: Grafana Docker
license: MIT
min_ansible_version: "2.4"
min_ansible_container_version: "2.4"
platforms:
- name: Debian
versions:
- buster
- bullseye
- name: Ubuntu
versions:
- bionic
- focal
- jammy
- name: Alpine
version:
- all
- name: ArchLinux
versions:
- all
galaxy_tags:
- grafana
- document
- wiki
- docker
dependencies: []

View File

@ -0,0 +1,15 @@
***********************************
Delegated driver installation guide
***********************************
Requirements
============
This driver is delegated to the developer. Up to the developer to implement
requirements.
Install
=======
This driver is delegated to the developer. Up to the developer to implement
requirements.

View File

@ -0,0 +1,23 @@
---
- name: Clean up
hosts: all
gather_facts: true
tasks:
- name: Check if the docker-compose file exists.
ansible.builtin.stat:
path: "~/grafana/docker-compose.yml"
register: docker_compose_file
- name: Remove docker-compose.
community.docker.docker_compose:
project_src: ~/grafana/
build: false
state: absent
when: docker_compose_file.stat.exists
become: false
- name: Remove the docker-compose file
ansible.builtin.file:
path: "~/grafana/docker-compose.yml"
state: absent
when: docker_compose_file.stat.exists

View File

@ -0,0 +1,11 @@
---
- name: Converge
hosts: all
# gather_facts: false
pre_tasks:
- name: "Include necessary variables"
ansible.builtin.include_vars:
file: "../../.env.yml"
roles:
- role: laurivan.grafana

View File

@ -0,0 +1,28 @@
---
- name: Destroy
hosts: localhost
connection: local
gather_facts: false
no_log: "{{ molecule_no_log }}"
tasks:
# Developer must implement.
- name: Remove the docker image
community.docker.docker_container:
name: instance-grafana
state: absent
# Mandatory configuration for Molecule to function.
- name: Populate instance config
ansible.builtin.set_fact:
instance_conf: {}
- name: Dump instance config
ansible.builtin.copy:
content: |
# Molecule managed
{{ instance_conf | to_json | from_json | to_yaml }}
dest: "{{ molecule_instance_config }}"
mode: 0600
when: server.changed | default(false) | bool

View File

@ -0,0 +1,30 @@
---
role_name_check: 1
dependency:
name: galaxy
options:
ignore-certs: true
ignore-errors: true
role-file: molecule/requirements.yml
requirements-file: molecule/requirements.yml
driver:
name: docker
platforms:
- name: instance-grafana
image: "geerlingguy/docker-${MOLECULE_DISTRO:-centos8}-ansible:latest"
command: ${MOLECULE_DOCKER_COMMAND:-""}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- /var/run/docker.sock:/tmp/docker_mounted.sock
privileged: true
pre_build_image: true
provisioner:
name: ansible
playbooks:
converge: ${MOLECULE_PLAYBOOK:-converge.yml}
verifier:
name: ansible
lint: |
set -e
yamllint .
ansible-lint .

View File

@ -0,0 +1,35 @@
---
- name: Setup the test machine
hosts: instance-grafana
tasks:
- name: Check if /var/run/docker.sock already exists
ansible.builtin.stat:
path: "/var/run/docker.sock"
register: docker_sock_stat
- name: Create docker.sock
raw: touch /var/run/docker.sock
become: true
changed_when: false
when: not docker_sock_stat.stat.exists
- name: Move docker.sock from tmp
raw: mount --move /tmp/docker_mounted.sock /var/run/docker.sock
become: true
changed_when: false
when: not docker_sock_stat.stat.exists
- name: Update apt cache.
apt: update_cache=yes cache_valid_time=600
when: ansible_os_family == 'Debian'
- name: Install python requests
pip:
name:
- requests
- docker
- docker-compose
- name: Install docker
vars:
docker_service_manage: false
include_role:
name: geerlingguy.docker

View File

@ -0,0 +1,10 @@
---
# This is an example playbook to execute Ansible tests.
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Example assertion
ansible.builtin.assert:
that: true

View File

@ -0,0 +1,4 @@
---
roles:
- geerlingguy.docker
collections: []

73
tasks/main.yml Normal file
View File

@ -0,0 +1,73 @@
---
# tasks file for grafana
- name: Set up main directory
ansible.builtin.file:
state: directory
path: "{{ item }}"
owner: "{{ ansible_effective_user_id }}"
group: "{{ ansible_effective_group_id }}"
mode: "0750"
with_items:
- "{{ grafana_setup_path | expanduser | realpath }}/grafana-provisioning/"
- "{{ grafana_setup_path | expanduser | realpath }}/grafana-provisioning/datasources/"
tags:
- configuration
become: true
- name: Set up influxdb mount point
ansible.builtin.file:
state: directory
path: "{{ item }}"
owner: "{% if grafana_uid %}{{ grafana_uid }}{% else %}{{ ansible_effective_user_id }}{% endif %}"
group: "{% if grafana_gid %}{{ grafana_gid }}{% else %}{{ ansible_effective_group_id }}{% endif %}"
mode: "0750"
with_items:
- "{{ grafana_volume_influxdb | expanduser | realpath }}"
tags:
- configuration
become: true
- name: Set up grafana mount point
ansible.builtin.file:
state: directory
path: "{{ item }}"
# owner: "{% if grafana_uid %}{{ grafana_uid }}{% else %}{{ ansible_effective_user_id }}{% endif %}"
# group: "{% if grafana_gid %}{{ grafana_gid }}{% else %}{{ ansible_effective_group_id }}{% endif %}"
owner: "472"
group: "0"
mode: "0777"
with_items:
- "{{ grafana_volume_grafana | expanduser | realpath }}"
- "{{ grafana_volume_grafana | expanduser | realpath }}/plugins"
tags:
- configuration
become: true
- name: Write configuration files
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "{{ grafana_setup_path | expanduser | realpath }}/{{ item }}"
mode: '0640'
loop:
- "docker-compose.yml"
- "env.grafana.conf"
- "env.influxdb.conf"
tags:
- configuration
- name: Write datasource provisioning files
ansible.builtin.template:
src: "grafana-provisioning/datasources/{{ item }}.j2"
dest: "{{ grafana_setup_path | expanduser | realpath }}/grafana-provisioning/datasources/{{ item }}"
mode: '0640'
loop:
- "datasource.yml"
tags:
- configuration
- name: Ensure all requested components are running.
community.docker.docker_compose:
project_src: "{{ grafana_setup_path | expanduser | realpath }}"
build: false
become: true

View File

@ -0,0 +1,33 @@
version: '3.9'
services:
influxdb:
image: influxdb:latest
ports:
- '8086:8086'
volumes:
- "{{ grafana_volume_influxdb | expanduser | realpath }}:/var/lib/influxdb"
env_file:
- "{{ grafana_setup_path | expanduser | realpath }}/env.influxdb.conf"
networks:
- grafana
grafana:
image: grafana/grafana-oss:latest
ports:
- '3001:3000'
volumes:
# - "{{ grafana_volume_grafana | expanduser | realpath }}:/var/lib/grafana"
- "{{ grafana_setup_path | expanduser | realpath }}/grafana-provisioning/:/etc/grafana/provisioning/"
depends_on:
- influxdb
env_file:
- "{{ grafana_setup_path | expanduser | realpath }}/env.grafana.conf"
networks:
- grafana
# Run as user:
# owner: "{% if grafana_uid %}{{ grafana_uid }}{% else %}{{ ansible_effective_user_id }}{% endif %}"
# group: "{% if grafana_gid %}{{ grafana_gid }}{% else %}{{ ansible_effective_group_id }}{% endif %}"
networks:
grafana: {}

View File

@ -0,0 +1,9 @@
GF_SECURITY_ADMIN_USER={{ grafana.username }}
GF_SECURITY_ADMIN_PASSWORD={{ grafana.password }}
GF_LOG_MODE=console
GF_LOG_LEVEL=debug
DOCKER_INFLUXDB_INIT_ORG={{ influxdb.org }}
DOCKER_INFLUXDB_INIT_BUCKET={{ influxdb.bucket }}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN={{ influxdb.admin_token }}

View File

@ -0,0 +1,6 @@
DOCKER_INFLUXDB_INIT_MODE=setup
DOCKER_INFLUXDB_INIT_USERNAME={{ influxdb.username }}
DOCKER_INFLUXDB_INIT_PASSWORD={{ influxdb.password }}
DOCKER_INFLUXDB_INIT_ORG={{ influxdb.org }}
DOCKER_INFLUXDB_INIT_BUCKET={{ influxdb.bucket }}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN={{ influxdb.admin_token }}

View File

@ -0,0 +1,15 @@
lines (14 sloc) 345 Bytes
apiVersion: 1
datasources:
- name: InfluxDB
version: 2
type: influxdb
access: proxy
url: http://influxdb:8086
jsonData:
defaultBucket: "{{ influxdb.bucket }}"
httpMode: POST
organization: "{{ influxdb.org }}"
version: Flux
secureJsonData:
token: "{{ influxdb.admin_token }}"

2
tests/inventory Normal file
View File

@ -0,0 +1,2 @@
localhost

5
tests/test.yml Normal file
View File

@ -0,0 +1,5 @@
---
- hosts: localhost
remote_user: root
roles:
- grafana

2
vars/main.yml Normal file
View File

@ -0,0 +1,2 @@
---
# vars file for grafana