commit 23a5ea6703fff42622eb0df562a1021384577733 Author: Laur Ivan Date: Mon Dec 19 14:18:31 2022 +0100 feat: Add grafana role. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..2da2ded --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: equinoxel +patreon: laurivan \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcb54e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.retry +*/__pycache__ +*.pyc +.cache +.venv +.env.yml +docker-compose.yml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..36bbf62 --- /dev/null +++ b/.travis.yml @@ -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/ \ No newline at end of file diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8827676 --- /dev/null +++ b/.yamllint @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37f76e9 --- /dev/null +++ b/LICENSE @@ -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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..395c6ef --- /dev/null +++ b/README.md @@ -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). diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..c2c7153 --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +# TODOs + +- [ ] Write a proper README. diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..5296015 --- /dev/null +++ b/defaults/main.yml @@ -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: diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..fc08820 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for grafana \ No newline at end of file diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..1c2af22 --- /dev/null +++ b/meta/main.yml @@ -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: [] diff --git a/molecule/default/INSTALL.rst b/molecule/default/INSTALL.rst new file mode 100644 index 0000000..1b38d09 --- /dev/null +++ b/molecule/default/INSTALL.rst @@ -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. diff --git a/molecule/default/cleanup.yml b/molecule/default/cleanup.yml new file mode 100644 index 0000000..9dd2516 --- /dev/null +++ b/molecule/default/cleanup.yml @@ -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 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..58baa67 --- /dev/null +++ b/molecule/default/converge.yml @@ -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 diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml new file mode 100644 index 0000000..045414f --- /dev/null +++ b/molecule/default/destroy.yml @@ -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 diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..2e79620 --- /dev/null +++ b/molecule/default/molecule.yml @@ -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 . \ No newline at end of file diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml new file mode 100644 index 0000000..851e50a --- /dev/null +++ b/molecule/default/prepare.yml @@ -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 diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml new file mode 100644 index 0000000..e707420 --- /dev/null +++ b/molecule/default/verify.yml @@ -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 diff --git a/molecule/requirements.yml b/molecule/requirements.yml new file mode 100644 index 0000000..a6284de --- /dev/null +++ b/molecule/requirements.yml @@ -0,0 +1,4 @@ +--- +roles: + - geerlingguy.docker +collections: [] diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..160d1ce --- /dev/null +++ b/tasks/main.yml @@ -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 diff --git a/templates/docker-compose.yml.j2 b/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..f2e4633 --- /dev/null +++ b/templates/docker-compose.yml.j2 @@ -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: {} diff --git a/templates/env.grafana.conf.j2 b/templates/env.grafana.conf.j2 new file mode 100644 index 0000000..8f8652d --- /dev/null +++ b/templates/env.grafana.conf.j2 @@ -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 }} \ No newline at end of file diff --git a/templates/env.influxdb.conf.j2 b/templates/env.influxdb.conf.j2 new file mode 100644 index 0000000..d87f7ab --- /dev/null +++ b/templates/env.influxdb.conf.j2 @@ -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 }} \ No newline at end of file diff --git a/templates/grafana-provisioning/datasources/datasource.yml.j2 b/templates/grafana-provisioning/datasources/datasource.yml.j2 new file mode 100644 index 0000000..d2f2d4a --- /dev/null +++ b/templates/grafana-provisioning/datasources/datasource.yml.j2 @@ -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 }}" \ No newline at end of file diff --git a/tests/inventory b/tests/inventory new file mode 100644 index 0000000..878877b --- /dev/null +++ b/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/tests/test.yml b/tests/test.yml new file mode 100644 index 0000000..300a834 --- /dev/null +++ b/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - grafana \ No newline at end of file diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..43674a4 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for grafana \ No newline at end of file