From bcbfd3928522d035e8068f587e85384d737f1eda Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Sun, 10 Nov 2024 13:31:35 +0100 Subject: [PATCH] feat: add automatic reload of consul service for certificate reloading This feature adds logic to automatically reload the consul service if tls is enbabled and the certificates have changed. This only tracks certificates copied by the extra_files logic. --- roles/consul/tasks/configure.yml | 66 ++++++++++++++++++++++++++++++++ roles/consul/tasks/install.yml | 2 +- roles/consul/tasks/main.yml | 16 +++++++- roles/consul/vars/main.yml | 13 +++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/roles/consul/tasks/configure.yml b/roles/consul/tasks/configure.yml index 6d5d535..7e31ab9 100644 --- a/roles/consul/tasks/configure.yml +++ b/roles/consul/tasks/configure.yml @@ -24,6 +24,31 @@ when: _consul_env_file.changed or _consul_config_file.changed +- name: "Consul | Gather initial checksums for certificate files" + ansible.builtin.stat: + path: "{{ item }}" + checksum_algorithm: sha1 + loop: "{{ consul_certificates_reload_watchlist }}" + when: consul_enable_tls + register: _consul_initial_cert_checksums + +- name: "Consul | Normalize initial checksums" + ansible.builtin.set_fact: + # This needs to be optimized, but I have spent so much time on it not + # working that I will keep it as is for now, and we'll see later. + _consul_initial_checksums_normalized: >- + {% filter trim %} + {% set checksums = [] %} + {% for item in _consul_initial_cert_checksums.results %} + {% set _ = checksums.append({ + 'item': item.item, + 'initial_checksum': (item.stat.checksum | default('absent')) + }) %} + {% endfor %} + {{ checksums }} + {% endfilter %} + when: consul_enable_tls + - name: "Consul | Copy extra configuration files" when: consul_extra_files block: @@ -72,3 +97,44 @@ loop_control: loop_var: dir_source_item when: _consul_dir_sources is defined + +- name: "Consul | Gather final checksums for certificate files" + ansible.builtin.stat: + path: "{{ item }}" + checksum_algorithm: sha1 + loop: "{{ consul_certificates_reload_watchlist }}" + when: consul_enable_tls + register: _consul_final_cert_checksums + +- name: "Consul | Normalize final checksums" + ansible.builtin.set_fact: + # This needs to be optimized, but I have spent so much time on it not + # working that I will keep it as is for now, and we'll see later. + _consul_final_checksums_normalized: >- + {% filter trim %} + {% set checksums = [] %} + {% for item in _consul_final_cert_checksums.results %} + {% set _ = checksums.append({ + 'item': item.item, + 'final_checksum': (item.stat.checksum | default('absent')) + }) %} + {% endfor %} + {{ checksums }} + {% endfilter %} + when: consul_enable_tls + +- name: "Consul | Merge initial and final checksum lists" + ansible.builtin.set_fact: + _consul_checksums_list: >- + {{ + _consul_initial_checksums_normalized | + community.general.lists_mergeby(_consul_final_checksums_normalized, 'item') + }} + when: consul_enable_tls + +- name: "Consul | Determine if certificates have changed or were newly added" + ansible.builtin.set_fact: + _consul_service_need_reload: true + when: + - consul_enable_tls + - _consul_checksums_list | json_query('[?initial_checksum!=final_checksum]') | list| length > 0 diff --git a/roles/consul/tasks/install.yml b/roles/consul/tasks/install.yml index 1d4bd18..1dbcd8e 100644 --- a/roles/consul/tasks/install.yml +++ b/roles/consul/tasks/install.yml @@ -136,6 +136,6 @@ - name: "Consul | Set reload-check & restart-check variable" ansible.builtin.set_fact: - _consul_service_need_reload: true + _consul_service_need_daemon_reload: true _consul_service_need_restart: true when: _consul_unit_file.changed # noqa: no-handler diff --git a/roles/consul/tasks/main.yml b/roles/consul/tasks/main.yml index 98654f2..2778a41 100644 --- a/roles/consul/tasks/main.yml +++ b/roles/consul/tasks/main.yml @@ -2,6 +2,7 @@ # task/main file for consul - name: "Consul | Set reload-check & restart-check variable" ansible.builtin.set_fact: + _consul_service_need_daemon_reload: false _consul_service_need_reload: false _consul_service_need_restart: false @@ -37,11 +38,22 @@ - name: "Consul | Reload systemd daemon" ansible.builtin.systemd: daemon_reload: true - when: _consul_service_need_reload + when: _consul_service_need_daemon_reload - name: "Consul | Start service: {{ consul_service_name }}" ansible.builtin.service: name: "{{ consul_service_name }}" state: restarted throttle: 1 - when: _consul_service_need_restart + when: + - consul_start_service + - _consul_service_need_restart + +- name: "Consul | Reload service: {{ consul_service_name }}" + ansible.builtin.service: + name: "{{ consul_service_name }}" + state: reloaded + throttle: 1 + when: + - _consul_service_need_reload + - not _consul_service_need_restart diff --git a/roles/consul/vars/main.yml b/roles/consul/vars/main.yml index 38cd5b5..b95d841 100644 --- a/roles/consul/vars/main.yml +++ b/roles/consul/vars/main.yml @@ -21,6 +21,19 @@ consul_github_project: hashicorp/consul consul_github_url: https://github.com consul_repository_url: https://releases.hashicorp.com/consul +consul_certificates_reload_watchlist: | + {% filter trim %} + {% set watchlist = [] %} + {% for block, config in (consul_configuration.tls | default({})).items() %} + {% for key in ['ca_file', 'cert_file', 'key_file'] %} + {% if config.get(key) %} + {{ watchlist.append(config[key]) }} + {% endif %} + {% endfor %} + {% endfor %} + {{ watchlist | unique }} + {% endfilter %} + consul_configuration: domain: "{{ consul_domain }}" datacenter: "{{ consul_datacenter }}"