diff --git a/ansible/host_vars/healthchecks/docker_values.yml b/ansible/host_vars/healthchecks/docker_values.yml index f7ec562d3..be73e71f0 100644 --- a/ansible/host_vars/healthchecks/docker_values.yml +++ b/ansible/host_vars/healthchecks/docker_values.yml @@ -1,6 +1,8 @@ --- healthchecks: port: "8000" + project_id: "{{ opconnect_results['healthchecks'].project_id }}" + readonly_apikey: "{{ opconnect_results['healthchecks'].readonly_apikey }}" healthcheck: "{{ opconnect_results['healthchecks'].healthcheck }}" email: host: smtp.gmail.com diff --git a/ansible/host_vars/monitoring/cloudflare.yml b/ansible/host_vars/monitoring/cloudflare.yml index 24403f23b..00adf549b 100644 --- a/ansible/host_vars/monitoring/cloudflare.yml +++ b/ansible/host_vars/monitoring/cloudflare.yml @@ -1,23 +1,11 @@ --- cloudflare_dns_records: - - zone: "{{ domain }}" - record: "{{ compose_project_name }}.{{ domain }}" - type: CNAME - value: tunnel-{{ ansible_host }}.{{ domain }} - proxied: true - state: present - zone: "{{ domain }}" record: grafana.{{ domain }} type: CNAME value: tunnel-{{ ansible_host }}.{{ domain }} proxied: true state: present - - zone: "{{ domain }}" - record: prometheus.{{ domain }} - type: CNAME - value: tunnel-{{ ansible_host }}.{{ domain }} - proxied: true - state: present - zone: "{{ domain }}" record: influxdb.{{ domain }} type: CNAME diff --git a/ansible/host_vars/monitoring/cloudflared.yml b/ansible/host_vars/monitoring/cloudflared.yml index c79b4659a..18df3d29d 100644 --- a/ansible/host_vars/monitoring/cloudflared.yml +++ b/ansible/host_vars/monitoring/cloudflared.yml @@ -1,8 +1,6 @@ --- additional_ingress: - hostname: grafana.{{ domain }} - service: http://localhost:{{ compose_project_ports.grafana[0] }} - - hostname: prometheus.{{ domain }} - service: http://localhost:{{ compose_project_ports.prometheus[0] }} + service: http://localhost:{{ grafana.port }} - hostname: influxdb.{{ domain }} - service: http://localhost:{{ compose_project_ports.influxdb[0] }} + service: http://localhost:{{ influxdb.port }} diff --git a/ansible/host_vars/monitoring/docker-compose.yaml b/ansible/host_vars/monitoring/docker-compose.yaml index 2f26f45ec..ac53f7439 100644 --- a/ansible/host_vars/monitoring/docker-compose.yaml +++ b/ansible/host_vars/monitoring/docker-compose.yaml @@ -1,32 +1,18 @@ --- version: "3" services: - prometheus: - image: prom/prometheus:latest@sha256:1a3e9a878e50cd339ae7cf5718fda08381dda2d4ccd28e94bbaa3190d1a566c2 - restart: always - volumes: - - "{{ docker_volume_dirs.prometheus[0] }}/prometheus.yml:/etc/prometheus/prometheus.yml" - - "{{ docker_volume_dirs.prometheus[1] }}:/prometheus" # todo: chown this after creation on host: chown 65534:65534 - ports: - - "{{ compose_project_ports.prometheus[0] }}:{{ prometheus.internal_port }}" - loki: - image: grafana/loki:latest@sha256:262d91088fb8cc31d3a6a0591aea52f74fa448884c961e63abd35decd3570a88 - restart: always - volumes: - - "{{ docker_volume_dirs.loki[0] }}/local-config.yaml:/etc/loki/local-config.yaml" - ports: - - "{{ compose_project_ports.loki[0] }}:3100" influxdb: - image: influxdb:2.6@sha256:d7c48214e57f77b8eba4e29d0c9b2a0c10393e7a9f9ee5d015da2d75b845faca + image: influxdb:1.8 restart: always volumes: - "{{ docker_volume_dirs.influxdb[0] }}:/var/lib/influxdb" ports: - - "{{ compose_project_ports.influxdb[0] }}:{{ influxdb.internal_port }}" + - "{{ influxdb.port }}:{{ influxdb.internal_port }}" varken: image: boerderij/varken:latest@sha256:784aaf577c822a3e69c5b2792e0422896c8cdb0455e584cd5a4e46bca789848d restart: always depends_on: + # Currently varken requires influxdb to be on v1.8.x - influxdb volumes: - "{{ docker_volume_dirs.varken[0] }}:/config" @@ -40,11 +26,10 @@ services: - "{{ docker_volume_dirs.grafana[1] }}:/var/lib/grafana" - "{{ docker_volume_dirs.grafana[2] }}:/var/lib/grafana/plugins" - "{{ docker_volume_dirs.grafana[3] }}:/etc/grafana/provisioning" + - "{{ docker_volume_dirs.grafana[4] }}:/var/lib/grafana/dashboards" ports: - - "{{ compose_project_ports.grafana[0] }}:3000" + - "{{ grafana.port }}:3000" depends_on: - - prometheus - - loki - influxdb - varken environment: diff --git a/ansible/host_vars/monitoring/docker.yml b/ansible/host_vars/monitoring/docker.yml index 6376908f7..897c8861a 100644 --- a/ansible/host_vars/monitoring/docker.yml +++ b/ansible/host_vars/monitoring/docker.yml @@ -1,10 +1,6 @@ --- compose_project_name: monitoring compose_project_ports: - prometheus: - - "{{ prometheus.port }}" - loki: - - "{{ loki.port }}" influxdb: - "{{ influxdb.port }}" grafana: @@ -16,11 +12,6 @@ docker_group_id: docker_volume_dirs: monitoring: - /mnt/{{ compose_project_name }} - prometheus: - - /mnt/{{ compose_project_name }}/prometheus/config - - /mnt/{{ compose_project_name }}/prometheus/data - loki: - - /mnt/{{ compose_project_name }}/loki/config influxdb: - /mnt/{{ compose_project_name }}/influxdb/config varken: @@ -30,7 +21,4 @@ docker_volume_dirs: - /mnt/{{ compose_project_name }}/grafana/data - /mnt/{{ compose_project_name }}/grafana/plugins - /mnt/{{ compose_project_name }}/grafana/provisioning - - /mnt/{{ compose_project_name }}/grafana/provisioning/datasources - - /mnt/{{ compose_project_name }}/grafana/provisioning/plugins - - /mnt/{{ compose_project_name }}/grafana/provisioning/notifiers - - /mnt/{{ compose_project_name }}/grafana/provisioning/dashboards + - /mnt/{{ compose_project_name }}/grafana/dashboards diff --git a/ansible/host_vars/monitoring/docker_values.yml b/ansible/host_vars/monitoring/docker_values.yml index 7ed3292e1..f49ca3d08 100644 --- a/ansible/host_vars/monitoring/docker_values.yml +++ b/ansible/host_vars/monitoring/docker_values.yml @@ -1,27 +1,29 @@ --- -prometheus: - host: prometheus - port: "9000" - internal_port: "9090" - cloudflared: - - firefly.{{ domain }} - - firefly-importer.{{ domain }} - - healthchecks.{{ domain }} - - prometheus.{{ domain }} - - grafana.{{ domain }} - - drive.thepi.cloud - - pihole.{{ domain }} - - vault.{{ domain }} - - allaboutsecurity.xyz - - securemy.life - healthchecks: - project_id: "{{ opconnect_results['healthchecks'].project_id }}" - readonly_apikey: "{{ opconnect_results['healthchecks'].readonly_apikey }}" -loki: - host: loki - port: "3100" grafana: port: "3000" + cloud: + enabled: false + org: "{{ opconnect_results['grafana'].cloud_tenant_name }}" + host: "{{ opconnect_results['grafana'].cloud_tenant_name }}.grafana.net" + services: + - prefix: am + name: "Alertmanager" + - prefix: hl + name: "Loki" + - prefix: hm + name: "Metrics" + subservices: + - prefix: Graphite + name: "Graphite" + - prefix: Prom + name: "Prometheus" + - prefix: ht + name: "Traces" + provisioning: + datasources: + api_path: "datasources" + dashboards: + api_path: "dashboards/home" plugins: grafana-piechart-panel,grafana-worldmap-panel,grafana-clock-panel admin: username: "{{ opconnect_results['grafana'].username }}" @@ -33,17 +35,10 @@ grafana: password: "{{ opconnect_results['grafana'].smtp_password }}" from: grafana@{{ domain }} provisioning: + alerting: datasources: apiVersion: 1 datasources: - - name: Loki - type: loki - access: proxy - url: http://{{ loki.host }}:{{ loki.port }} - - name: Prometheus - type: prometheus - access: proxy - url: http://{{ prometheus.host }}:{{ prometheus.internal_port }} - name: Influxdb type: influxdb access: proxy @@ -58,7 +53,7 @@ grafana: database: varken username: "{{ influxdb.username }}" password: "{{ influxdb.password }}" - dashboardProviders: + dashboards: apiVersion: 1 providers: - name: default @@ -85,41 +80,43 @@ grafana: editable: true options: path: /var/lib/grafana/dashboards/media + notifiers: + plugins: dashboards: default: prometheus: gnetId: 2 revision: 2 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" pihole: gnetId: 10176 revision: 2 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" unifi: client-insights: gnetId: 11315 revision: 8 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" uap-insights: gnetId: 11314 revision: 9 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" network-sites: gnetId: 11311 revision: 4 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" usw-insights: gnetId: 11312 revision: 8 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" usg-insights: gnetId: 11313 revision: 8 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" client-dpi: gnetId: 11310 revision: 4 - datasource: Prometheus + datasource: "grafanacloud-{{ opconnect_results['grafana'].cloud_tenant_name }}-prom" media: varken-official: gnetId: 9585 diff --git a/ansible/playbooks/codeserver.yml b/ansible/playbooks/codeserver.yml index fcbeb4a1e..0a6550836 100644 --- a/ansible/playbooks/codeserver.yml +++ b/ansible/playbooks/codeserver.yml @@ -23,4 +23,5 @@ - dns - healthchecks - cron + - grafana_agent - codeserver diff --git a/ansible/playbooks/gaming.yml b/ansible/playbooks/gaming.yml index c559d946d..298a0f60b 100644 --- a/ansible/playbooks/gaming.yml +++ b/ansible/playbooks/gaming.yml @@ -21,4 +21,5 @@ - iptables - dns - healthchecks + - grafana_agent - docker diff --git a/ansible/playbooks/healthchecks.yml b/ansible/playbooks/healthchecks.yml index 1307a8e67..fff032c0a 100644 --- a/ansible/playbooks/healthchecks.yml +++ b/ansible/playbooks/healthchecks.yml @@ -9,6 +9,8 @@ - password - smtp_username - smtp_password + - project_id + - readonly_apikey roles: - opconnect @@ -21,4 +23,5 @@ - cloudflare - iptables - dns + - grafana_agent - docker diff --git a/ansible/playbooks/monitoring.yml b/ansible/playbooks/monitoring.yml index c8ec6f84a..1d0f3e6f1 100644 --- a/ansible/playbooks/monitoring.yml +++ b/ansible/playbooks/monitoring.yml @@ -8,6 +8,9 @@ - password - smtp_username - smtp_password + - cloud_tenant_name + - cloud_admin_token + - cloud_service_account_token influxdb: - username - password @@ -24,8 +27,6 @@ - ombi_apikey - maxmind_license_key healthchecks: - - project_id - - readonly_apikey - apikey roles: - opconnect @@ -41,8 +42,7 @@ - dns - healthchecks - cron - - loki - - prometheus + - grafana_agent - grafana - varken - docker diff --git a/ansible/playbooks/pihole.yml b/ansible/playbooks/pihole.yml index 12409ac87..15dd3bb57 100644 --- a/ansible/playbooks/pihole.yml +++ b/ansible/playbooks/pihole.yml @@ -21,5 +21,6 @@ - iptables - dns - healthchecks + - grafana_agent - pihole - docker diff --git a/ansible/playbooks/roles/grafana/tasks/dashboard.yml b/ansible/playbooks/roles/grafana/tasks/dashboard.yml new file mode 100644 index 000000000..b8e380475 --- /dev/null +++ b/ansible/playbooks/roles/grafana/tasks/dashboard.yml @@ -0,0 +1,63 @@ +--- +# The grafana_dashboard.value might be a couple types of dashboards +# Currently, only dashboards with the gnetId and revision values are supported +# For dashboards with the gnetId and revision, we want to download the dashboard as JSON from https://grafana.com/api/dashboards//revisions//download +- name: Download dashboard {{ grafana_dashboard.key }} from Grafana.com + ansible.builtin.uri: + url: https://grafana.com/api/dashboards/{{ grafana_dashboard.value.gnetId }}/revisions/{{ grafana_dashboard.value.revision }}/download + return_content: yes + status_code: 200 + register: dashboard_json_result + no_log: true + when: grafana_dashboard.value.gnetId is defined and grafana_dashboard.value.revision is defined + +- name: Parse dashboard JSON + ansible.builtin.set_fact: + dashboard_json: "{{ dashboard_json_result.json }}" + +# For some dashboards, the datasource is configured in a datasource annotation in the annotations list +# This list should be updated to match the configured datasource (if set) + +# First, get list of annotations +- name: Get list of annotations + ansible.builtin.set_fact: + grafana_dashboard_annotations: "{{ dashboard_json.annotations.list | default([]) | list }}" + +# Then, find any annotations that have the 'datasource' key +- name: Find annotations with datasource key + when: grafana_dashboard_annotations | length > 0 + ansible.builtin.set_fact: + grafana_dashboard_annotations: "{{ grafana_dashboard_annotations | selectattr('datasource', 'defined') | list }}" + +- name: Set updated_grafana_dashboard_annotations to empty list + ansible.builtin.set_fact: + updated_grafana_dashboard_annotations: [] + +- name: Create datasource fact + ansible.builtin.set_fact: + grafana_dashboard_datasource: + datasource: "{{ grafana_dashboard.value.datasource }}" + +# Then, update the datasource value to match the configured datasource +# We want to override the datasource value in place and in order too +- name: Update datasource value in annotations + when: grafana_dashboard_annotations | length > 0 + ansible.builtin.set_fact: + updated_grafana_dashboard_annotations: "{{ updated_grafana_dashboard_annotations + [grafana_dashboard_annotation | combine(grafana_dashboard_datasource) ] }}" + loop: "{{ grafana_dashboard_annotations }}" + loop_control: + loop_var: grafana_dashboard_annotation + +# Finally, update the dashboard JSON with the updated annotations +- name: Update dashboard JSON with updated annotations + ansible.builtin.set_fact: + dashboard_json: "{{ dashboard_json | combine({'annotations': dashboard_json.annotations | combine({'list': updated_grafana_dashboard_annotations})}, recursive=True) }}" + when: updated_grafana_dashboard_annotations is defined and updated_grafana_dashboard_annotations | length > 0 + +# For dashboards with the gnetId and revision, we want to write the JSON to a file +# The file will be written to the provider directory in /mnt/monitoring/grafana/dashboards/ +- name: Write dashboard {{ grafana_dashboard.key }} to file + ansible.builtin.copy: + dest: "{{ grafana_dashboard_provider_path }}/{{ grafana_dashboard.key }}.json" + content: "{{ dashboard_json | to_nice_json(indent=2) }}" + when: grafana_dashboard.value.gnetId is defined and grafana_dashboard.value.revision is defined diff --git a/ansible/playbooks/roles/grafana/tasks/dashboards.yml b/ansible/playbooks/roles/grafana/tasks/dashboards.yml new file mode 100644 index 000000000..cf816596e --- /dev/null +++ b/ansible/playbooks/roles/grafana/tasks/dashboards.yml @@ -0,0 +1,20 @@ +--- +# Create provider directory in /mnt/monitoring/grafana/dashboards +- name: Create provider dashboard directory for {{ grafana_dashboard_provider.key }} + become: true + ansible.builtin.file: + path: "{{ docker_volume_dirs.grafana[4] }}/{{ grafana_dashboard_provider.key }}" + state: directory + +# Set variable for full dashboard provider path +- name: Set dashboard provider path + ansible.builtin.set_fact: + grafana_dashboard_provider_path: "{{ docker_volume_dirs.grafana[4] }}/{{ grafana_dashboard_provider.key }}" + +# Loop through the dashboards associated with the provider and include the dashboard.yml task +- name: Create dashboard files for {{ grafana_dashboard_provider.key }} + ansible.builtin.include_tasks: dashboard.yml + loop: "{{ grafana_dashboard_provider.value | dict2items }}" + loop_control: + loop_var: grafana_dashboard + no_log: true diff --git a/ansible/playbooks/roles/grafana/tasks/grafana_cloud.yml b/ansible/playbooks/roles/grafana/tasks/grafana_cloud.yml new file mode 100644 index 000000000..cb8be4524 --- /dev/null +++ b/ansible/playbooks/roles/grafana/tasks/grafana_cloud.yml @@ -0,0 +1,102 @@ +--- +- name: Get instances from Grafana Cloud API using cloud_admin_token + ansible.builtin.uri: + url: "https://grafana.com/api/orgs/{{ grafana.cloud.org }}/instances" + method: GET + headers: + Authorization: "Bearer {{ opconnect_results['grafana'].cloud_admin_token }}" + Content-Type: "application/json" + register: grafana_cloud_instances + +- name: Find instance with name that matches grafana cloud url + ansible.builtin.set_fact: + grafana_cloud_instance: "{{ grafana_cloud_instance }}" + loop: "{{ grafana_cloud_instances.json['items'] }}" + loop_control: + loop_var: grafana_cloud_instance + when: grafana_cloud_instance.name == grafana.cloud.host + no_log: true + +# is a single object with a bunch of key-value pairs +# Some keys start with a prefix value that matches a prefix in +# The result should be a map of to new objects, +# where each object contains all the key-value pairs from that start with the +# Using the json query provided by jmespath and a starts_with query, we can filter the object to only include key:value entries with keys that start with the +- name: Create map of Grafana Cloud services + loop: "{{ grafana.cloud.services }}" + loop_control: + loop_var: service + no_log: true + ansible.builtin.set_fact: + grafana_cloud_services: "{{ grafana_cloud_services | default({}) | combine({ service.name: grafana_cloud_instance | dict2items | json_query('[?starts_with(@.key, `'+service.prefix+'`)]') | list | default([]) | items2dict }) }}" + +# - debug: +# msg: "{{ grafana_cloud_services }}" + +# Update each object of each service so that the keys of each object do not include the service's prefix +# For example, if the service prefix is "am", then the key "am.url" should be changed to "url" in the "am" service object +# This means that {"am": {"amUrl": "google.com"}} would become {"am": {"Url": "google.com"}} +- name: Remove service prefix from keys + loop: "{{ grafana.cloud.services }}" + loop_control: + loop_var: service + no_log: true + ansible.builtin.set_fact: + grafana_cloud_services: "{{ grafana_cloud_services | default({}) | combine({ service.name: dict(grafana_cloud_services[service.name] | dict2items | map(attribute='key') | map('regex_replace', '^'+service.prefix+'Instance', '') | list | zip(grafana_cloud_services[service.name] | dict2items | map(attribute='value') | list)) }) }}" + +# - debug: +# msg: "{{ grafana_cloud_services }}" + +# Update each service object with subservices to have one sub object per sub service +- name: Create map of Grafana Cloud sub services + no_log: true + loop: "{{ grafana.cloud.services }}" + loop_control: + loop_var: service + when: service.subservices is defined + ansible.builtin.set_fact: + grafana_cloud_sub_services: "{{ grafana_cloud_sub_services | default({}) | combine({ service.name: dict(grafana_cloud_services[service.name] | dict2items | map(attribute='key') | map('regex_replace', '^'+service.prefix, '') | list | zip(grafana_cloud_services[service.name] | dict2items | map(attribute='value') | list)) }) }}" +# - debug: +# msg: "{{ grafana_cloud_sub_services }}" + +# - name: Test if Grafana Cloud URL is accessible +# ansible.builtin.uri: +# url: "https://{{ grafana.cloud.host }}/api/health" +# method: GET +# headers: +# Content-Type: "application/json" +# register: grafana_cloud_url_test +# failed_when: grafana_cloud_url_test.status != 200 +# - name: Test if Grafana Cloud is accessible with service_account_token +# ansible.builtin.uri: +# url: "https://{{ grafana.cloud.host }}/api/auth/keys" +# method: GET +# headers: +# Authorization: "Bearer {{ opconnect_results['grafana'].cloud_service_account_token }}" +# Content-Type: "application/json" +# register: grafana_cloud_test +# failed_when: grafana_cloud_test.status != 200 +# - name: Retrieve result for {{ grafana_provisioning.key }} from API +# ansible.builtin.uri: +# url: "https://{{ grafana.cloud.host }}/api/{{ grafana_provisioning.value.api_path }}" +# method: GET +# headers: +# Authorization: "Bearer {{ opconnect_results['grafana'].cloud_service_account_token }}" +# Content-Type: "application/json" +# register: grafana_cloud_provisioning +# failed_when: grafana_cloud_provisioning.status != 200 + +# - debug: +# msg: "{{ grafana_cloud_provisioning.json }}" + +# - name: Get full details of {{ grafana_provisioning.key }} from API using uid value +# loop: "{{ grafana_cloud_provisioning.json }}" +# loop_control: +# loop_var: grafana_cloud_provisioning_item +# ansible.builtin.uri: +# url: "https://{{ grafana.cloud.host }}/api/{{ grafana_provisioning.value.api_path }}/uid/{{ grafana_cloud_provisioning_item.uid }}" +# method: GET +# headers: +# Authorization: "Bearer {{ opconnect_results['grafana'].cloud_service_account_token }}" +# Content-Type: "application/json" +# register: grafana_cloud_provisioning_uid diff --git a/ansible/playbooks/roles/grafana/tasks/main.yml b/ansible/playbooks/roles/grafana/tasks/main.yml index ce6ab39f7..7d388df4e 100644 --- a/ansible/playbooks/roles/grafana/tasks/main.yml +++ b/ansible/playbooks/roles/grafana/tasks/main.yml @@ -24,3 +24,20 @@ loop: "{{ grafana.provisioning | dict2items }}" loop_control: loop_var: grafana_provisioning + no_log: true + +- name: Setup Grafana dashboards + ansible.builtin.include_tasks: dashboards.yml + when: grafana.dashboards is defined + loop: "{{ grafana.dashboards | dict2items }}" + loop_control: + loop_var: grafana_dashboard_provider + no_log: true + +- name: Provision Grafana Cloud Content + include_tasks: grafana_cloud.yml + when: grafana.cloud.enabled + loop: "{{ grafana.cloud.provisioning | dict2items }}" + loop_control: + loop_var: grafana_provisioning + no_log: true diff --git a/ansible/playbooks/roles/grafana/tasks/provisioning.yml b/ansible/playbooks/roles/grafana/tasks/provisioning.yml index 08374db95..b9f040fd3 100644 --- a/ansible/playbooks/roles/grafana/tasks/provisioning.yml +++ b/ansible/playbooks/roles/grafana/tasks/provisioning.yml @@ -9,4 +9,5 @@ become: true ansible.builtin.copy: dest: "{{ docker_volume_dirs.grafana[3] }}/{{ grafana_provisioning.key }}/config.yaml" - content: "{{ grafana_provisioning.value }}" + content: "{{ grafana_provisioning.value | to_nice_yaml(indent=2, width=1337) }}" + when: grafana_provisioning.value is defined diff --git a/ansible/playbooks/roles/grafana_agent/tasks/main.yml b/ansible/playbooks/roles/grafana_agent/tasks/main.yml new file mode 100644 index 000000000..bae5df22b --- /dev/null +++ b/ansible/playbooks/roles/grafana_agent/tasks/main.yml @@ -0,0 +1,101 @@ +--- +# Load current grafana-agent configuration from file on remote host +# grafana-agent configuration is at /etc/grafana-agent.yaml and sudo is needed to read it +- name: Load current grafana-agent configuration from file on remote host + become: true + ansible.builtin.slurp: + src: /etc/grafana-agent.yaml + register: grafana_agent_config + ignore_errors: true + +# Parse config to YAML +- name: Parse config to YAML + ansible.builtin.set_fact: + grafana_agent_config: "{{ grafana_agent_config['content'] | b64decode | from_yaml }}" + +# Replace any occurrences of the string with the current host's hostname +- name: Replace with current host's hostname + ansible.builtin.set_fact: + grafana_agent_config: "{{ grafana_agent_config | replace('', ansible_hostname) }}" + +- name: Set fact for cloudflared job + ansible.builtin.set_fact: + cloudflared_scrape_config: + job_name: cloudflared + metrics_path: /metrics + scheme: http + static_configs: + - targets: + - localhost:2000 + labels: + instance: "{{ ansible_hostname }}" + +- name: Set fact for pihole job + ansible.builtin.set_fact: + pihole_scrape_config: + job_name: "pihole" + static_configs: + - targets: + - localhost:{{ pihole_exporter.port }} + labels: + instance: "{{ ansible_hostname }}" + when: ansible_hostname == 'pihole' + +- name: Set fact for healthchecks job + ansible.builtin.set_fact: + healthchecks_scrape_config: + job_name: "healthchecks" + scheme: http + metrics_path: "/projects/{{ healthchecks.project_id }}/metrics/{{ healthchecks.readonly_apikey }}" + static_configs: + - targets: + - "localhost:{{ healthchecks.port }}" + labels: + instance: "{{ ansible_hostname }}" + when: ansible_hostname == 'healthchecks' + +- name: Update cloudflared job + ansible.builtin.include_tasks: + file: update_scrape_job.yml + apply: + vars: + input_job_name: cloudflared + input_scrape_config: "{{ cloudflared_scrape_config }}" + +- name: Update pihole job + ansible.builtin.include_tasks: + file: update_scrape_job.yml + apply: + vars: + input_job_name: pihole + input_scrape_config: "{{ pihole_scrape_config }}" + when: ansible_hostname == 'pihole' + +- name: Update healthchecks job + ansible.builtin.include_tasks: + file: update_scrape_job.yml + apply: + vars: + input_job_name: healthchecks + input_scrape_config: "{{ healthchecks_scrape_config }}" + when: ansible_hostname == 'healthchecks' + +# Update grafana-agent configuration file on remote host +# grafana-agent configuration is at /etc/grafana-agent.yaml and sudo is needed to write it +- name: Update grafana-agent configuration file on remote host + become: true + ansible.builtin.copy: + content: "{{ grafana_agent_config | to_nice_yaml(indent=2, width=1337) }}" + dest: /etc/grafana-agent.yaml + owner: root + group: root + mode: 0644 + register: grafana_agent_config_update + +# Restart grafana-agent service +- name: Restart grafana-agent service + become: true + ansible.builtin.service: + name: grafana-agent + state: restarted + when: grafana_agent_config_update is changed diff --git a/ansible/playbooks/roles/grafana_agent/tasks/update_scrape_job.yml b/ansible/playbooks/roles/grafana_agent/tasks/update_scrape_job.yml new file mode 100644 index 000000000..b6c401618 --- /dev/null +++ b/ansible/playbooks/roles/grafana_agent/tasks/update_scrape_job.yml @@ -0,0 +1,21 @@ +--- +# Update metrics config to have a 'input_job_name' job, as defined by the input_scrape_config variable +# This job might already exist, so we need to check for that by looking at the 'scrape_configs' list +# If the job already exists, we need to update it +# If the job doesn't exist, we need to add it +# The job might be at any index in the list, so we need to loop through the list to find it + +- name: Find {{ input_job_name }} job in metrics config + ansible.builtin.set_fact: + input_jobs_search: "{{ grafana_agent_config['metrics']['configs'][0]['scrape_configs'] | selectattr('job_name', 'equalto', input_job_name) | list }}" + when: grafana_agent_config['metrics']['configs'][0]['scrape_configs'] is defined + +- name: Create updated scrape_configs fact with {{ input_job_name }} job + ansible.builtin.set_fact: + updated_scrape_configs: "{{ grafana_agent_config['metrics']['configs'][0]['scrape_configs'] | difference(input_jobs_search) | list + [input_scrape_config] }}" + when: grafana_agent_config['metrics']['configs'][0]['scrape_configs'] is defined and input_jobs_search | length >= 0 + +- name: Update metrics config with {{ input_job_name }} job + ansible.builtin.set_fact: + grafana_agent_config: "{{ grafana_agent_config | combine({'metrics': grafana_agent_config['metrics'] | combine({'configs': [grafana_agent_config['metrics']['configs'][0] | combine({'scrape_configs': updated_scrape_configs})]})}) }}" + when: updated_scrape_configs is defined diff --git a/ansible/playbooks/roles/healthchecks/tasks/healthchecks.yml b/ansible/playbooks/roles/healthchecks/tasks/healthchecks.yml index c19a52b74..5dc316907 100644 --- a/ansible/playbooks/roles/healthchecks/tasks/healthchecks.yml +++ b/ansible/playbooks/roles/healthchecks/tasks/healthchecks.yml @@ -27,6 +27,7 @@ - 201 register: healthchecks_create +# TODO: update this so that final healthcheck uses http:// tailscale IP :8000 for healtchecks domain - name: Update healthcheck ansible.builtin.set_fact: healthchecks: diff --git a/ansible/playbooks/roles/prometheus/templates/prometheus.yml.j2 b/ansible/playbooks/roles/prometheus/templates/prometheus.yml.j2 index 65778bf14..2bdeb717a 100644 --- a/ansible/playbooks/roles/prometheus/templates/prometheus.yml.j2 +++ b/ansible/playbooks/roles/prometheus/templates/prometheus.yml.j2 @@ -22,20 +22,3 @@ scrape_configs: static_configs: - targets: - "homeassistant:8123" -- job_name: 'pihole' - static_configs: - - targets: - - "{{ pihole_exporter.host }}:{{ pihole_exporter.port }}" -- job_name: 'healthchecks' - scheme: http - metrics_path: "/projects/{{ prometheus.healthchecks.project_id }}/metrics/{{ prometheus.healthchecks.readonly_apikey }}" - static_configs: - - targets: - - "healthchecks:{{ hostvars['healthchecks'].healthchecks.port }}" -- job_name: 'cloudflared' - scheme: https - static_configs: - - targets: -{% for item in prometheus.cloudflared %} - - "{{ item }}" -{% endfor %} diff --git a/ansible/playbooks/vault.yml b/ansible/playbooks/vault.yml index 283771801..2ed2d3983 100644 --- a/ansible/playbooks/vault.yml +++ b/ansible/playbooks/vault.yml @@ -17,5 +17,6 @@ - cloudflare - dns - healthchecks + - grafana_agent - vault - docker