diff --git a/molecule/consul_default/converge.yml b/molecule/consul_default/converge.yml new file mode 100644 index 0000000..91712f2 --- /dev/null +++ b/molecule/consul_default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + become: true + tasks: + - name: "Include ednz_cloud.hashistack.consul" + ansible.builtin.include_role: + name: "ednz_cloud.hashistack.consul" diff --git a/molecule/consul_default/molecule.yml b/molecule/consul_default/molecule.yml new file mode 100644 index 0000000..8314c8e --- /dev/null +++ b/molecule/consul_default/molecule.yml @@ -0,0 +1,37 @@ +--- +dependency: + name: galaxy + options: + requirements-file: ./requirements.yml +driver: + name: docker +platforms: + - name: instance + image: geerlingguy/docker-${MOLECULE_TEST_OS}-ansible + command: "" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup + cgroupns_mode: host + privileged: true + pre_build_image: true +provisioner: + name: ansible + config_options: + defaults: + remote_tmp: /tmp/.ansible +verifier: + name: ansible +scenario: + name: consul_default + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/consul_default/prepare.yml b/molecule/consul_default/prepare.yml new file mode 100644 index 0000000..c216743 --- /dev/null +++ b/molecule/consul_default/prepare.yml @@ -0,0 +1,13 @@ +--- +- name: Prepare + hosts: all + become: true + tasks: + - name: "Install pre-required system packages" + ansible.builtin.include_role: + name: ednz_cloud.manage_apt_packages + vars: + manage_apt_packages_list: + - name: unzip + version: latest + state: present diff --git a/molecule/consul_default/requirements.yml b/molecule/consul_default/requirements.yml new file mode 100644 index 0000000..329e789 --- /dev/null +++ b/molecule/consul_default/requirements.yml @@ -0,0 +1,4 @@ +--- +# requirements file for molecule +roles: + - name: ednz_cloud.manage_apt_packages diff --git a/molecule/consul_default/verify.yml b/molecule/consul_default/verify.yml new file mode 100644 index 0000000..db7c5d1 --- /dev/null +++ b/molecule/consul_default/verify.yml @@ -0,0 +1,170 @@ +--- +- name: Verify + hosts: all + gather_facts: true + become: true + tasks: + - name: "Test: consul user and group" + block: + - name: "Getent user consul" + ansible.builtin.getent: + database: passwd + key: consul + register: consul_user + + - name: "Getent group consul" + ansible.builtin.getent: + database: group + key: consul + register: consul_group + + - name: "Verify consul user and group" + ansible.builtin.assert: + that: + - not consul_user.failed + - not consul_group.failed + - "'consul' in consul_user.ansible_facts.getent_passwd.keys()" + - "'/home/consul' in consul_user.ansible_facts.getent_passwd['consul']" + - "'/bin/false' in consul_user.ansible_facts.getent_passwd['consul']" + - "'consul' in consul_group.ansible_facts.getent_group.keys()" + + - name: "Test: binary /usr/local/bin/consul" + block: + - name: "Stat binary /usr/local/bin/consul" + ansible.builtin.stat: + path: "/usr/local/bin/consul" + register: stat_usr_local_bin_consul + + - name: "Verify binary /usr/local/bin/consul" + ansible.builtin.assert: + that: + - stat_usr_local_bin_consul.stat.exists + - stat_usr_local_bin_consul.stat.isreg + - stat_usr_local_bin_consul.stat.pw_name == 'root' + - stat_usr_local_bin_consul.stat.gr_name == 'root' + - stat_usr_local_bin_consul.stat.mode == '0755' + + - name: "Test: directory /etc/consul.d" + block: + - name: "Stat directory /etc/consul.d" + ansible.builtin.stat: + path: "/etc/consul.d" + register: stat_etc_consul_d + + - name: "Stat file /etc/consul.d/consul.env" + ansible.builtin.stat: + path: "/etc/consul.d/consul.env" + register: stat_etc_consul_d_consul_env + + - name: "Stat file /etc/consul.d/consul.json" + ansible.builtin.stat: + path: "/etc/consul.d/consul.json" + register: stat_etc_consul_d_consul_json + + - name: "Slurp file /etc/consul.d/consul.json" + ansible.builtin.slurp: + src: "/etc/consul.d/consul.json" + register: slurp_etc_consul_d_consul_json + + - name: "Verify directory /etc/consul.d" + ansible.builtin.assert: + that: + - stat_etc_consul_d.stat.exists + - stat_etc_consul_d.stat.isdir + - stat_etc_consul_d.stat.pw_name == 'consul' + - stat_etc_consul_d.stat.gr_name == 'consul' + - stat_etc_consul_d.stat.mode == '0755' + - stat_etc_consul_d_consul_env.stat.exists + - stat_etc_consul_d_consul_env.stat.isreg + - stat_etc_consul_d_consul_env.stat.pw_name == 'consul' + - stat_etc_consul_d_consul_env.stat.gr_name == 'consul' + - stat_etc_consul_d_consul_env.stat.mode == '0600' + - stat_etc_consul_d_consul_json.stat.exists + - stat_etc_consul_d_consul_json.stat.isreg + - stat_etc_consul_d_consul_json.stat.pw_name == 'consul' + - stat_etc_consul_d_consul_json.stat.gr_name == 'consul' + - stat_etc_consul_d_consul_json.stat.mode == '0600' + - slurp_etc_consul_d_consul_json.content != '' + + - name: "Test: directory /opt/consul" + block: + - name: "Stat directory /opt/consul" + ansible.builtin.stat: + path: "/opt/consul" + register: stat_opt_consul + + - name: "Verify directory /opt/consul" + ansible.builtin.assert: + that: + - stat_opt_consul.stat.exists + - stat_opt_consul.stat.isdir + - stat_opt_consul.stat.pw_name == 'consul' + - stat_opt_consul.stat.gr_name == 'consul' + - stat_opt_consul.stat.mode == '0755' + + - name: "Test: service consul" + block: + - name: "Get service consul" + ansible.builtin.service_facts: + + - name: "Stat file /etc/systemd/system/consul.service" + ansible.builtin.stat: + path: "/etc/systemd/system/consul.service" + register: stat_etc_systemd_system_consul_service + + - name: "Slurp file /etc/systemd/system/consul.service" + ansible.builtin.slurp: + src: "/etc/systemd/system/consul.service" + register: slurp_etc_systemd_system_consul_service + + - name: "Verify service consul" + ansible.builtin.assert: + that: + - stat_etc_systemd_system_consul_service.stat.exists + - stat_etc_systemd_system_consul_service.stat.isreg + - stat_etc_systemd_system_consul_service.stat.pw_name == 'root' + - stat_etc_systemd_system_consul_service.stat.gr_name == 'root' + - stat_etc_systemd_system_consul_service.stat.mode == '0644' + - slurp_etc_systemd_system_consul_service.content != '' + - ansible_facts.services['consul.service'] is defined + - ansible_facts.services['consul.service']['source'] == 'systemd' + - ansible_facts.services['consul.service']['state'] == 'running' + - ansible_facts.services['consul.service']['status'] == 'enabled' + + - name: "Test: interaction consul" + block: + - name: "Command consul kv put" + ansible.builtin.command: "consul kv put foo bar" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + changed_when: false + register: consul_kv_put + + - name: "Command consul kv get" + ansible.builtin.command: "consul kv get foo" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + changed_when: false + register: consul_kv_get + + - name: "Command consul kv delete" + ansible.builtin.command: "consul kv delete foo" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + changed_when: false + register: consul_kv_delete + + - name: "Command consul members" + ansible.builtin.command: "consul members" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + changed_when: false + register: consul_members + + - name: "Verify consul interaction" + ansible.builtin.assert: + that: + - "'instance' in consul_members.stdout" + - consul_kv_put.stdout == 'Success! Data written to: foo' + - consul_kv_get.stdout == 'bar' + - consul_kv_delete.stdout == 'Success! Deleted key: foo' diff --git a/molecule/consul_with_acl_enabled/converge.yml b/molecule/consul_with_acl_enabled/converge.yml new file mode 100644 index 0000000..91712f2 --- /dev/null +++ b/molecule/consul_with_acl_enabled/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + become: true + tasks: + - name: "Include ednz_cloud.hashistack.consul" + ansible.builtin.include_role: + name: "ednz_cloud.hashistack.consul" diff --git a/molecule/consul_with_acl_enabled/group_vars/all.yml b/molecule/consul_with_acl_enabled/group_vars/all.yml new file mode 100644 index 0000000..ff14db7 --- /dev/null +++ b/molecule/consul_with_acl_enabled/group_vars/all.yml @@ -0,0 +1,23 @@ +--- +##################### +# ACL configuration # +##################### + +consul_acl_configuration: + enabled: true + default_policy: "deny" + enable_token_persistence: true + tokens: + initial_management: "1a1f2ce5-3730-47de-9a9c-89e037376bab" + agent: "1a1f2ce5-3730-47de-9a9c-89e037376bab" + +########### +# logging # +########### + +consul_log_level: info +consul_enable_log_to_file: true +consul_log_to_file_configuration: + log_file: "{{ consul_logs_dir }}/consul.log" + log_rotate_duration: 24h + log_rotate_max_files: 30 diff --git a/molecule/consul_with_acl_enabled/molecule.yml b/molecule/consul_with_acl_enabled/molecule.yml new file mode 100644 index 0000000..82508a0 --- /dev/null +++ b/molecule/consul_with_acl_enabled/molecule.yml @@ -0,0 +1,37 @@ +--- +dependency: + name: galaxy + options: + requirements-file: ./requirements.yml +driver: + name: docker +platforms: + - name: instance + image: geerlingguy/docker-${MOLECULE_TEST_OS}-ansible + command: "" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup + cgroupns_mode: host + privileged: true + pre_build_image: true +provisioner: + name: ansible + config_options: + defaults: + remote_tmp: /tmp/.ansible +verifier: + name: ansible +scenario: + name: consul_with_acl_enabled + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/consul_with_acl_enabled/prepare.yml b/molecule/consul_with_acl_enabled/prepare.yml new file mode 100644 index 0000000..c216743 --- /dev/null +++ b/molecule/consul_with_acl_enabled/prepare.yml @@ -0,0 +1,13 @@ +--- +- name: Prepare + hosts: all + become: true + tasks: + - name: "Install pre-required system packages" + ansible.builtin.include_role: + name: ednz_cloud.manage_apt_packages + vars: + manage_apt_packages_list: + - name: unzip + version: latest + state: present diff --git a/molecule/consul_with_acl_enabled/requirements.yml b/molecule/consul_with_acl_enabled/requirements.yml new file mode 100644 index 0000000..329e789 --- /dev/null +++ b/molecule/consul_with_acl_enabled/requirements.yml @@ -0,0 +1,4 @@ +--- +# requirements file for molecule +roles: + - name: ednz_cloud.manage_apt_packages diff --git a/molecule/consul_with_acl_enabled/verify.yml b/molecule/consul_with_acl_enabled/verify.yml new file mode 100644 index 0000000..8ae0242 --- /dev/null +++ b/molecule/consul_with_acl_enabled/verify.yml @@ -0,0 +1,176 @@ +--- +- name: Verify + hosts: all + gather_facts: true + become: true + tasks: + - name: "Test: consul user and group" + block: + - name: "Getent user consul" + ansible.builtin.getent: + database: passwd + key: consul + register: consul_user + + - name: "Getent group consul" + ansible.builtin.getent: + database: group + key: consul + register: consul_group + + - name: "Verify consul user and group" + ansible.builtin.assert: + that: + - not consul_user.failed + - not consul_group.failed + - "'consul' in consul_user.ansible_facts.getent_passwd.keys()" + - "'/home/consul' in consul_user.ansible_facts.getent_passwd['consul']" + - "'/bin/false' in consul_user.ansible_facts.getent_passwd['consul']" + - "'consul' in consul_group.ansible_facts.getent_group.keys()" + + - name: "Test: binary /usr/local/bin/consul" + block: + - name: "Stat binary /usr/local/bin/consul" + ansible.builtin.stat: + path: "/usr/local/bin/consul" + register: stat_usr_local_bin_consul + + - name: "Verify binary /usr/local/bin/consul" + ansible.builtin.assert: + that: + - stat_usr_local_bin_consul.stat.exists + - stat_usr_local_bin_consul.stat.isreg + - stat_usr_local_bin_consul.stat.pw_name == 'root' + - stat_usr_local_bin_consul.stat.gr_name == 'root' + - stat_usr_local_bin_consul.stat.mode == '0755' + + - name: "Test: directory /etc/consul.d" + block: + - name: "Stat directory /etc/consul.d" + ansible.builtin.stat: + path: "/etc/consul.d" + register: stat_etc_consul_d + + - name: "Stat file /etc/consul.d/consul.env" + ansible.builtin.stat: + path: "/etc/consul.d/consul.env" + register: stat_etc_consul_d_consul_env + + - name: "Stat file /etc/consul.d/consul.json" + ansible.builtin.stat: + path: "/etc/consul.d/consul.json" + register: stat_etc_consul_d_consul_json + + - name: "Slurp file /etc/consul.d/consul.json" + ansible.builtin.slurp: + src: "/etc/consul.d/consul.json" + register: slurp_etc_consul_d_consul_json + + - name: "Verify directory /etc/consul.d" + ansible.builtin.assert: + that: + - stat_etc_consul_d.stat.exists + - stat_etc_consul_d.stat.isdir + - stat_etc_consul_d.stat.pw_name == 'consul' + - stat_etc_consul_d.stat.gr_name == 'consul' + - stat_etc_consul_d.stat.mode == '0755' + - stat_etc_consul_d_consul_env.stat.exists + - stat_etc_consul_d_consul_env.stat.isreg + - stat_etc_consul_d_consul_env.stat.pw_name == 'consul' + - stat_etc_consul_d_consul_env.stat.gr_name == 'consul' + - stat_etc_consul_d_consul_env.stat.mode == '0600' + - stat_etc_consul_d_consul_json.stat.exists + - stat_etc_consul_d_consul_json.stat.isreg + - stat_etc_consul_d_consul_json.stat.pw_name == 'consul' + - stat_etc_consul_d_consul_json.stat.gr_name == 'consul' + - stat_etc_consul_d_consul_json.stat.mode == '0600' + - slurp_etc_consul_d_consul_json.content != '' + + - name: "Test: directory /opt/consul" + block: + - name: "Stat directory /opt/consul" + ansible.builtin.stat: + path: "/opt/consul" + register: stat_opt_consul + + - name: "Verify directory /opt/consul" + ansible.builtin.assert: + that: + - stat_opt_consul.stat.exists + - stat_opt_consul.stat.isdir + - stat_opt_consul.stat.pw_name == 'consul' + - stat_opt_consul.stat.gr_name == 'consul' + - stat_opt_consul.stat.mode == '0755' + + - name: "Test: service consul" + block: + - name: "Get service consul" + ansible.builtin.service_facts: + + - name: "Stat file /etc/systemd/system/consul.service" + ansible.builtin.stat: + path: "/etc/systemd/system/consul.service" + register: stat_etc_systemd_system_consul_service + + - name: "Slurp file /etc/systemd/system/consul.service" + ansible.builtin.slurp: + src: "/etc/systemd/system/consul.service" + register: slurp_etc_systemd_system_consul_service + + - name: "Verify service consul" + ansible.builtin.assert: + that: + - stat_etc_systemd_system_consul_service.stat.exists + - stat_etc_systemd_system_consul_service.stat.isreg + - stat_etc_systemd_system_consul_service.stat.pw_name == 'root' + - stat_etc_systemd_system_consul_service.stat.gr_name == 'root' + - stat_etc_systemd_system_consul_service.stat.mode == '0644' + - slurp_etc_systemd_system_consul_service.content != '' + - ansible_facts.services['consul.service'] is defined + - ansible_facts.services['consul.service']['source'] == 'systemd' + - ansible_facts.services['consul.service']['state'] == 'running' + - ansible_facts.services['consul.service']['status'] == 'enabled' + + - name: "Test: interaction consul" + vars: + acl_token: "1a1f2ce5-3730-47de-9a9c-89e037376bab" + block: + - name: "Command consul kv put" + ansible.builtin.command: "consul kv put foo bar" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + CONSUL_HTTP_TOKEN: "{{ acl_token }}" + changed_when: false + register: consul_kv_put + + - name: "Command consul kv get" + ansible.builtin.command: "consul kv get foo" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + CONSUL_HTTP_TOKEN: "{{ acl_token }}" + changed_when: false + register: consul_kv_get + + - name: "Command consul kv delete" + ansible.builtin.command: "consul kv delete foo" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + CONSUL_HTTP_TOKEN: "{{ acl_token }}" + changed_when: false + register: consul_kv_delete + + - name: "Command consul members" + ansible.builtin.command: "consul members" + environment: + CONSUL_HTTP_ADDR: "http://{{ ansible_default_ipv4.address }}:8500" + CONSUL_HTTP_TOKEN: "{{ acl_token }}" + changed_when: false + register: consul_members + + - name: "Verify consul interaction" + ansible.builtin.assert: + that: + - "'instance' in consul_members.stdout" + - consul_kv_put.stdout == 'Success! Data written to: foo' + - consul_kv_get.stdout == 'bar' + - consul_kv_delete.stdout == 'Success! Deleted key: foo' diff --git a/roles/consul/defaults/main.yml b/roles/consul/defaults/main.yml new file mode 100644 index 0000000..9f43d7b --- /dev/null +++ b/roles/consul/defaults/main.yml @@ -0,0 +1,146 @@ +--- +# defaults file for hashicorp_consul + +consul_version: "latest" +consul_start_service: true +consul_config_dir: "/etc/consul.d" +consul_data_dir: "/opt/consul" +consul_certs_dir: "{{ consul_config_dir }}/tls" +consul_logs_dir: "/var/log/consul" + +consul_envoy_install: false +consul_envoy_version: latest + +consul_extra_files: false +consul_extra_files_list: [] + +consul_env_variables: {} + +####################### +# extra configuration # +####################### + +# You should prioritize adding configuration +# to the configuration entries below, this +# option should be used to add pieces of configuration not +# available through standard variables. + +consul_extra_configuration: {} + +########### +# general # +########### + +consul_domain: consul +consul_datacenter: dc1 +consul_primary_datacenter: "{{ consul_datacenter }}" +consul_gossip_encryption_key: "{{ 'mysupersecretgossipencryptionkey'|b64encode }}" +consul_enable_script_checks: false + +####################### +# leave configuration # +####################### + +consul_leave_on_terminate: true +consul_rejoin_after_leave: true + +###################### +# join configuration # +###################### + +consul_join_configuration: + retry_join: + - "{{ ansible_default_ipv4.address }}" + retry_interval: 30s + retry_max: 0 + +######################## +# server configuration # +######################## + +consul_enable_server: true +consul_bootstrap_expect: 1 + +#################### +# ui configuration # +#################### + +consul_ui_configuration: + enabled: "{{ consul_enable_server }}" + +######################### +# address configuration # +######################### + +consul_bind_addr: "0.0.0.0" +consul_advertise_addr: "{{ ansible_default_ipv4.address }}" +consul_address_configuration: + client_addr: "{{ consul_bind_addr }}" + bind_addr: "{{ consul_advertise_addr }}" + advertise_addr: "{{ consul_advertise_addr }}" + +##################### +# ACL configuration # +##################### + +consul_acl_configuration: + enabled: false + default_policy: "deny" + enable_token_persistence: true + # tokens: + # agent: "" + +############################## +# service mesh configuration # +############################## + +consul_mesh_configuration: + enabled: false + +##################### +# DNS configuration # +##################### + +consul_dns_configuration: + allow_stale: true + enable_truncate: true + only_passing: true + +################ +# internal tls # +################ + +consul_enable_tls: false +consul_tls_configuration: + defaults: + ca_file: "/etc/ssl/certs/ca-certificates.crt" + cert_file: "{{ consul_certs_dir }}/cert.pem" + key_file: "{{ consul_certs_dir }}/key.pem" + verify_incoming: false + verify_outgoing: true + internal_rpc: + verify_server_hostname: true + +consul_certificates_extra_files_dir: + [] + # - src: "" + # dest: "{{ consul_certs_dir }}" + +########################### +# telemetry configuration # +########################### + +consul_enable_prometheus_metrics: false +consul_prometheus_retention_time: 60s +consul_telemetry_configuration: {} + +########### +# logging # +########### + +consul_log_level: info +consul_enable_log_to_file: false +consul_log_to_file_configuration: + log_file: "{{ consul_logs_dir }}/consul.log" + log_rotate_duration: 24h + log_rotate_max_files: 30 diff --git a/roles/consul/handlers/main.yml b/roles/consul/handlers/main.yml new file mode 100644 index 0000000..7296134 --- /dev/null +++ b/roles/consul/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for hashicorp_consul diff --git a/roles/consul/meta/main.yml b/roles/consul/meta/main.yml new file mode 100644 index 0000000..965aeef --- /dev/null +++ b/roles/consul/meta/main.yml @@ -0,0 +1,26 @@ +--- +# meta file for hashicorp_consul +galaxy_info: + namespace: "ednz_cloud" + role_name: "hashicorp_consul" + author: "Bertrand Lanson" + description: "Install and configure hashicorp consul for debian-based distros." + license: "license (BSD, MIT)" + min_ansible_version: "2.10" + platforms: + - name: Ubuntu + versions: + - focal + - jammy + - noble + - name: Debian + versions: + - bullseye + - bookworm + galaxy_tags: + - "ubuntu" + - "debian" + - "hashicorp" + - "consul" + +dependencies: [] diff --git a/roles/consul/tasks/configure.yml b/roles/consul/tasks/configure.yml new file mode 100644 index 0000000..22bdcb1 --- /dev/null +++ b/roles/consul/tasks/configure.yml @@ -0,0 +1,74 @@ +--- +# task/configure file for hashicorp_consul +- name: "Consul | Create consul.env" + ansible.builtin.template: + src: consul.env.j2 + dest: "{{ consul_config_dir }}/consul.env" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0600" + register: _consul_env_file + +- name: "Consul | Copy consul.json template" + ansible.builtin.template: + src: consul.json.j2 + dest: "{{ consul_config_dir }}/consul.json" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0600" + register: _consul_config_file + +- name: "Consul | Set restart-check variable" + ansible.builtin.set_fact: + _consul_service_need_restart: true + when: _consul_env_file.changed or + _consul_config_file.changed + +- name: "Consul | Copy extra configuration files" + when: consul_extra_files + block: + - name: "Consul | Get extra file types" + ansible.builtin.stat: + path: "{{ item.src }}" + loop: "{{ consul_extra_files_list }}" + register: consul_extra_file_stat + delegate_to: localhost + + - name: "Consul | Set list for file sources" + vars: + _consul_file_sources: [] + ansible.builtin.set_fact: + _consul_file_sources: "{{ _consul_file_sources + [item.item] }}" + when: item.stat.isreg + loop: "{{ consul_extra_file_stat.results }}" + loop_control: + loop_var: item + delegate_to: localhost + + - name: "Consul | Set list for directory sources" + vars: + _consul_dir_sources: [] + ansible.builtin.set_fact: + _consul_dir_sources: "{{ _consul_dir_sources + [item.item] }}" + when: item.stat.isdir + loop: "{{ consul_extra_file_stat.results }}" + loop_control: + loop_var: item + delegate_to: localhost + + - name: "Consul | Template extra file sources" + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest | regex_replace('\\.j2$', '') }}" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0700" + loop: "{{ _consul_file_sources }}" + when: _consul_file_sources is defined + + - name: "Consul | Template extra directory sources" + ansible.builtin.include_tasks: recursive_copy_extra_dirs.yml + loop: "{{ _consul_dir_sources }}" + loop_control: + loop_var: dir_source_item + when: _consul_dir_sources is defined diff --git a/roles/consul/tasks/install.yml b/roles/consul/tasks/install.yml new file mode 100644 index 0000000..dab00b7 --- /dev/null +++ b/roles/consul/tasks/install.yml @@ -0,0 +1,141 @@ +--- +# task/install file for hashicorp_consul +- name: "Consul | Get latest release of consul" + when: consul_version == 'latest' + block: + - name: "Consul | Get latest consul release from github api" + ansible.builtin.uri: + url: "{{ consul_github_api }}/{{ consul_github_project }}/releases/latest" + return_content: true + register: _consul_latest_release + + - name: "Consul | Set wanted consul version to latest tag" + ansible.builtin.set_fact: + _consul_wanted_version: "{{ _consul_latest_release.json['tag_name']|regex_replace('v', '') }}" + +- name: "Consul | Set wanted consul version to {{ consul_version }}" + ansible.builtin.set_fact: + _consul_wanted_version: "{{ consul_version|regex_replace('v', '') }}" + when: consul_version != 'latest' + +- name: "Consul | Get current consul version" + block: + - name: "Consul | Stat consul version file" + ansible.builtin.stat: + path: "{{ consul_config_dir }}/.version" + changed_when: false + check_mode: false + register: _consul_version_file + + - name: "Consul | Get current consul version" + ansible.builtin.slurp: + src: "{{ _consul_version_file.stat.path }}" + when: + - _consul_version_file.stat.exists + - _consul_version_file.stat.isreg + register: _consul_current_version + +- name: "Consul | Download and install consul binary" + when: _consul_current_version is not defined + or _consul_wanted_version != (_consul_current_version.content|default('')|b64decode) + block: + - name: "Consul | Set consul package name to download" + ansible.builtin.set_fact: + _consul_package_name: >- + consul_{{ _consul_wanted_version }}_linux_{{ consul_deb_architecture_map[ansible_architecture] }}.zip + _consul_shasum_file_name: >- + consul_{{ _consul_wanted_version }}_SHA256SUMS + + - name: "Consul | Download checksum file for consul archive" + ansible.builtin.get_url: + url: "{{ consul_repository_url }}/{{ _consul_wanted_version }}/{{ _consul_shasum_file_name }}" + dest: "/tmp/{{ _consul_shasum_file_name }}" + mode: "0644" + register: _consul_checksum_file + until: _consul_checksum_file is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "Consul | Extract correct checksum from checksum file" + ansible.builtin.command: + cmd: 'grep "{{ _consul_package_name }}" /tmp/{{ _consul_shasum_file_name }}' + changed_when: false + register: _consul_expected_checksum_line + + - name: "Consul | Parse the expected checksum" + ansible.builtin.set_fact: + _consul_expected_checksum: "{{ _consul_expected_checksum_line.stdout.split()[0] }}" + + - name: "Consul | Download consul binary archive" + ansible.builtin.get_url: + url: "{{ consul_repository_url }}/{{ _consul_wanted_version }}/{{ _consul_package_name }}" + dest: "/tmp/{{ _consul_package_name }}" + mode: "0644" + checksum: "sha256:{{ _consul_expected_checksum }}" + register: _consul_binary_archive + until: _consul_binary_archive is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "Consul | Create temporary directory for archive decompression" + ansible.builtin.file: + path: /tmp/consul + state: directory + mode: "0755" + + - name: "Consul | Unpack consul archive" + ansible.builtin.unarchive: + src: "/tmp/{{ _consul_package_name }}" + dest: "/tmp/consul" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0755" + remote_src: true + + - name: "Consul | Copy consul binary to {{ consul_binary_path }}" + ansible.builtin.copy: + src: /tmp/consul/consul + dest: "{{ consul_binary_path }}" + owner: root + group: root + mode: "0755" + remote_src: true + force: true + + - name: "Consul | Update consul version file" + ansible.builtin.copy: + content: "{{ _consul_wanted_version }}" + dest: "{{ consul_config_dir }}/.version" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0600" + + - name: "Consul | Set restart-check variable" + ansible.builtin.set_fact: + _consul_service_need_restart: true + + - name: "Consul | Cleanup temporary directory" + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /tmp/consul + - /tmp/{{ _consul_package_name }} + - /tmp/{{ _consul_shasum_file_name }} + +- name: "Consul | Copy systemd service file for consul" + ansible.builtin.template: + src: "consul.service.j2" + dest: "/etc/systemd/system/{{ consul_service_name }}.service" + owner: root + group: root + mode: "0644" + register: _consul_unit_file + +- name: "Consul | Set reload-check & restart-check variable" + ansible.builtin.set_fact: + _consul_service_need_reload: true + _consul_service_need_restart: true + when: _consul_unit_file.changed # noqa: no-handler diff --git a/roles/consul/tasks/install_envoy.yml b/roles/consul/tasks/install_envoy.yml new file mode 100644 index 0000000..98ea19d --- /dev/null +++ b/roles/consul/tasks/install_envoy.yml @@ -0,0 +1,65 @@ +--- +# task/install_envoy file for hashicorp_consul +- name: "Consul | Get release for envoy:{{ consul_envoy_version }}" + vars: + _envoy_url_ext: "{% if consul_envoy_version == 'latest'%}releases{% else %}releases/tags{% endif %}" + ansible.builtin.uri: + url: "{{ consul_github_api }}/{{ consul_envoy_github_project }}/{{ _envoy_url_ext }}/{{ consul_envoy_version }}" + return_content: true + register: _envoy_new_release + +- name: "Consul | Check if envoy is already installed" + ansible.builtin.stat: + path: "{{ consul_data_dir }}/envoy/version" + changed_when: false + check_mode: false + register: _envoy_is_installed + +- name: "Consul | Check current envoy version" + ansible.builtin.command: "cat {{ consul_data_dir }}/envoy/version" + changed_when: false + check_mode: false + register: _envoy_old_release + when: _envoy_is_installed.stat.exists + +- name: "Consul | Set facts for wanted envoy release" + ansible.builtin.set_fact: + consul_envoy_wanted_version: "{{ _envoy_new_release.json['tag_name']|regex_replace('v', '') }}" + when: _envoy_new_release.json is defined + and (_envoy_new_release.json | length > 0) + +- name: "Consul | Set facts for current envoy release" + ansible.builtin.set_fact: + consul_envoy_current_version: "{{ _envoy_old_release.stdout | regex_replace('v', '') }}" + when: _envoy_old_release.stdout is defined + and (_envoy_old_release.stdout | length > 0) + +- name: "Consul | Create envoy directory" + ansible.builtin.file: + path: "{{ consul_data_dir }}/envoy" + state: directory + mode: "0775" + +- name: "Consul | Install envoy" + when: consul_envoy_current_version is not defined + or consul_envoy_wanted_version not in consul_envoy_current_version + block: + - name: "Consul | Remove old compose binary if different" + ansible.builtin.file: + path: "{{ consul_envoy_binary_path }}" + state: absent + register: _envoy_binary_removed + + - name: "Consul | Download and install envoy version:{{ consul_envoy_version }}" + ansible.builtin.get_url: + url: "{{ consul_github_url }}/{{ consul_envoy_github_project }}/releases/download/v{{ consul_envoy_wanted_version }}/envoy-{{ consul_envoy_wanted_version }}-linux-{{ consul_envoy_architecture }} " + dest: "{{ consul_envoy_binary_path }}" + owner: root + group: root + mode: "0755" + + - name: "Consul | Update version file" + ansible.builtin.copy: + content: "{{ consul_envoy_wanted_version }}" + dest: "{{ consul_data_dir }}/envoy/version" + mode: "0600" diff --git a/roles/consul/tasks/main.yml b/roles/consul/tasks/main.yml new file mode 100644 index 0000000..cb4fcb0 --- /dev/null +++ b/roles/consul/tasks/main.yml @@ -0,0 +1,47 @@ +--- +# task/main file for hashicorp_consul +- name: "Consul | Set reload-check & restart-check variable" + ansible.builtin.set_fact: + _consul_service_need_reload: false + _consul_service_need_restart: false + +- name: "Consul | Import merge_variables.yml" + ansible.builtin.include_tasks: merge_variables.yml + +- name: "Consul | Import prerequisites.yml" + ansible.builtin.include_tasks: prerequisites.yml + +- name: "Consul | Import install_envoy.yml" + ansible.builtin.include_tasks: install_envoy.yml + when: consul_envoy_install + +- name: "Consul | Import install.yml" + ansible.builtin.include_tasks: "install.yml" + +- name: "Consul | Import configure.yml" + ansible.builtin.include_tasks: configure.yml + +- name: "Consul | Populate service facts" + ansible.builtin.service_facts: + +- name: "Consul | Set restart-check variable" + ansible.builtin.set_fact: + _consul_service_need_restart: true + when: ansible_facts.services[consul_service_name~'.service'].state != 'running' + +- name: "Consul | Enable service: {{ consul_service_name }}" + ansible.builtin.service: + name: "{{ consul_service_name }}" + enabled: true + +- name: "Consul | Reload systemd daemon" + ansible.builtin.systemd: + daemon_reload: true + when: _consul_service_need_reload + +- name: "Consul | Start service: {{ consul_service_name }}" + ansible.builtin.service: + name: "{{ consul_service_name }}" + state: restarted + throttle: 1 + when: _consul_service_need_restart diff --git a/roles/consul/tasks/merge_variables.yml b/roles/consul/tasks/merge_variables.yml new file mode 100644 index 0000000..7f1a689 --- /dev/null +++ b/roles/consul/tasks/merge_variables.yml @@ -0,0 +1,94 @@ +--- +# task/merge_variables file for hashicorp_consul +- name: "Consul | Merge stringified configuration" + vars: + _config_to_merge: "{{ consul_configuration_string }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge|from_yaml, recursive=true) + }}" + +- name: "Consul | Merge server specific stringified configuration" + vars: + _config_to_merge: "{{ consul_server_configuration_string }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge|from_yaml, recursive=true) + }}" + when: + - consul_enable_server + +- name: "Consul | Merge addresses configuration" + vars: + _config_to_merge: "{{ consul_address_configuration }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Consul | Merge TLS configuration" + when: consul_enable_tls + block: + - name: "Consul | Merge TLS configuration" + vars: + _config_to_merge: + tls: "{{ consul_tls_configuration }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Consul | Add certificates directory to extra_files_dir" + ansible.builtin.set_fact: + consul_extra_files_list: "{{ + consul_extra_files_list + + consul_certificates_extra_files_dir + | unique + | sort + }}" + +- name: "Consul | Merge extra configuration settings" + vars: + _config_to_merge: "{{ consul_extra_configuration }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Consul | Merge log to file configuration" + vars: + _config_to_merge: "{{ consul_log_to_file_configuration }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: consul_enable_log_to_file + +- name: "Consul | Merge telemetry configuration" + block: + - name: "Consul | Merge prometheus metrics configuration" + vars: + _config_to_merge: + prometheus_retention_time: "{{ consul_prometheus_retention_time }}" + ansible.builtin.set_fact: + consul_telemetry_configuration: "{{ + consul_telemetry_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: consul_enable_prometheus_metrics + + - name: "Consul | Merge telemtry configuration" + vars: + _config_to_merge: + telemetry: "{{ consul_telemetry_configuration }}" + ansible.builtin.set_fact: + consul_configuration: "{{ + consul_configuration | + combine(_config_to_merge, recursive=true) + }}" diff --git a/roles/consul/tasks/prerequisites.yml b/roles/consul/tasks/prerequisites.yml new file mode 100644 index 0000000..1e34e83 --- /dev/null +++ b/roles/consul/tasks/prerequisites.yml @@ -0,0 +1,46 @@ +--- +# task/prerequisites file for hashicorp_consul +- name: "Consul | Create group {{ consul_group }}" + ansible.builtin.group: + name: "{{ consul_group }}" + state: present + +- name: "Consul | Create user {{ consul_user }}" + ansible.builtin.user: + name: "{{ consul_user }}" + group: "{{ consul_group }}" + shell: /bin/false + state: present + +- name: "Consul | Create directory {{ consul_config_dir }}" + ansible.builtin.file: + path: "{{ consul_config_dir }}" + state: directory + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0755" + +- name: "Consul | Create directory {{ consul_data_dir}}" + ansible.builtin.file: + path: "{{ consul_data_dir }}" + state: directory + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0755" + +- name: "Consul | Create directory {{ consul_certs_dir }}" + ansible.builtin.file: + path: "{{ consul_certs_dir }}" + state: directory + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0755" + +- name: "Consul | Create directory {{ consul_logs_dir }}" + ansible.builtin.file: + path: "{{ consul_logs_dir }}" + state: directory + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0755" + when: consul_enable_log_to_file diff --git a/roles/consul/tasks/recursive_copy_extra_dirs.yml b/roles/consul/tasks/recursive_copy_extra_dirs.yml new file mode 100644 index 0000000..09f43e4 --- /dev/null +++ b/roles/consul/tasks/recursive_copy_extra_dirs.yml @@ -0,0 +1,27 @@ +--- +# task/recursive_copy_extra_dirs file for hashicorp_consul +- name: "Consul | Ensure destination directory exists" + ansible.builtin.file: + path: "{{ dir_source_item.dest }}" + recurse: true + state: directory + mode: "0775" + +- name: "Consul | Create extra directory sources" + ansible.builtin.file: + path: "{{ dir_source_item.dest }}/{{ item.path }}" + recurse: true + state: directory + mode: "0775" + with_community.general.filetree: "{{ dir_source_item.src }}/" + when: item.state == 'directory' + +- name: "Consul | Template extra directory sources" + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ dir_source_item.dest }}/{{ item.path | regex_replace('\\.j2$', '') }}" + owner: "{{ consul_user }}" + group: "{{ consul_group }}" + mode: "0700" + with_community.general.filetree: "{{ dir_source_item.src }}/" + when: item.state == 'file' diff --git a/roles/consul/templates/consul.env.j2 b/roles/consul/templates/consul.env.j2 new file mode 100644 index 0000000..f5f982c --- /dev/null +++ b/roles/consul/templates/consul.env.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} +{% for item in consul_env_variables %} +{{ item }}="{{ consul_env_variables[item] }}" +{% endfor %} diff --git a/roles/consul/templates/consul.json.j2 b/roles/consul/templates/consul.json.j2 new file mode 100644 index 0000000..9bc6d5f --- /dev/null +++ b/roles/consul/templates/consul.json.j2 @@ -0,0 +1 @@ +{{ consul_configuration|to_nice_json }} diff --git a/roles/consul/templates/consul.service.j2 b/roles/consul/templates/consul.service.j2 new file mode 100644 index 0000000..5b1cd38 --- /dev/null +++ b/roles/consul/templates/consul.service.j2 @@ -0,0 +1,20 @@ +[Unit] +Description=Consul +Documentation=https://developer.hashicorp.com/consul/docs +Requires=network-online.target +After=network-online.target +ConditionFileNotEmpty={{ consul_config_dir }}/consul.json + +[Service] +EnvironmentFile=-{{ consul_config_dir }}/consul.env +User={{ consul_user }} +Group={{ consul_group }} +ExecStart={{ consul_binary_path }} agent -config-dir={{ consul_config_dir }} +ExecReload=/bin/kill --signal HUP $MAINPID +KillMode=process +KillSignal=SIGTERM +Restart=on-failure +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/roles/consul/vars/main.yml b/roles/consul/vars/main.yml new file mode 100644 index 0000000..f2139b1 --- /dev/null +++ b/roles/consul/vars/main.yml @@ -0,0 +1,58 @@ +--- +# vars file for hashicorp_consul +consul_user: consul +consul_group: consul +consul_binary_path: /usr/local/bin/consul +consul_envoy_binary_path: /usr/local/bin/envoy +consul_deb_architecture_map: + x86_64: "amd64" + aarch64: "arm64" + armv7l: "arm" + armv6l: "arm" +consul_envoy_architecture_map: + x86_64: "x86_64" + aarch64: "aarch64" +consul_architecture: "{{ consul_deb_architecture_map[ansible_architecture] | default(ansible_architecture) }}" +consul_envoy_architecture: "{{ consul_envoy_architecture_map[ansible_architecture] | default(ansible_architecture) }}" +consul_service_name: "consul" +consul_github_api: https://api.github.com/repos +consul_envoy_github_project: envoyproxy/envoy +consul_github_project: hashicorp/consul +consul_github_url: https://github.com +consul_repository_url: https://releases.hashicorp.com/consul + +consul_configuration: + domain: "{{ consul_domain }}" + datacenter: "{{ consul_datacenter }}" + primary_datacenter: "{{ consul_primary_datacenter }}" + data_dir: "{{ consul_data_dir }}" + encrypt: "{{ consul_gossip_encryption_key }}" + server: "{{ consul_enable_server }}" + ui_config: "{{ consul_ui_configuration }}" + connect: "{{ consul_mesh_configuration }}" + leave_on_terminate: "{{ consul_leave_on_terminate }}" + rejoin_after_leave: "{{ consul_rejoin_after_leave }}" + enable_script_checks: "{{ consul_enable_script_checks }}" + enable_syslog: true + acl: "{{ consul_acl_configuration }}" + dns_config: "{{ consul_dns_configuration }}" + log_level: "{{ consul_log_level }}" + ports: + dns: 8600 + server: 8300 + serf_lan: 8301 + serf_wan: 8302 + sidecar_min_port: 21000 + sidecar_max_port: 21255 + expose_min_port: 21500 + expose_max_port: 21755 + +consul_configuration_string: | + ports: + http: {{ ('8500'|int) if not consul_enable_tls else ('-1' | int) }} + https: {{ ('8501'|int) if consul_enable_tls else ('-1' | int) }} + grpc: {{ ('8502'|int) if not consul_enable_tls else ('-1' | int) }} + grpc_tls: {{ ('8503'|int) if consul_enable_tls else ('-1' | int) }} + +consul_server_configuration_string: | + bootstrap_expect: {{ consul_bootstrap_expect }}