diff --git a/molecule/nomad_default/converge.yml b/molecule/nomad_default/converge.yml new file mode 100644 index 0000000..ea6946e --- /dev/null +++ b/molecule/nomad_default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + become: true + tasks: + - name: "Include ednz_cloud.hashistack.nomad" + ansible.builtin.include_role: + name: "ednz_cloud.hashistack.nomad" diff --git a/molecule/nomad_default/molecule.yml b/molecule/nomad_default/molecule.yml new file mode 100644 index 0000000..d22a13c --- /dev/null +++ b/molecule/nomad_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: nomad_default + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/nomad_default/prepare.yml b/molecule/nomad_default/prepare.yml new file mode 100644 index 0000000..c216743 --- /dev/null +++ b/molecule/nomad_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/nomad_default/requirements.yml b/molecule/nomad_default/requirements.yml new file mode 100644 index 0000000..d3761e2 --- /dev/null +++ b/molecule/nomad_default/requirements.yml @@ -0,0 +1,5 @@ +--- +# requirements file for molecule +roles: + - name: ednz_cloud.manage_repositories + - name: ednz_cloud.manage_apt_packages diff --git a/molecule/nomad_default/verify.yml b/molecule/nomad_default/verify.yml new file mode 100644 index 0000000..765cff5 --- /dev/null +++ b/molecule/nomad_default/verify.yml @@ -0,0 +1,170 @@ +--- +- name: Verify + hosts: all + gather_facts: true + become: true + tasks: + - name: "Test: nomad user and group" + block: + - name: "Getent user nomad" + ansible.builtin.getent: + database: passwd + key: nomad + register: nomad_user + + - name: "Getent group nomad" + ansible.builtin.getent: + database: group + key: nomad + register: nomad_group + + - name: "Verify nomad user and group" + ansible.builtin.assert: + that: + - not nomad_user.failed + - not nomad_group.failed + - "'nomad' in nomad_user.ansible_facts.getent_passwd.keys()" + - "'/home/nomad' in nomad_user.ansible_facts.getent_passwd['nomad']" + - "'/bin/false' in nomad_user.ansible_facts.getent_passwd['nomad']" + - "'nomad' in nomad_group.ansible_facts.getent_group.keys()" + + - name: "Test: binary /usr/local/bin/nomad" + block: + - name: "Stat binary /usr/local/bin/nomad" + ansible.builtin.stat: + path: "/usr/local/bin/nomad" + register: stat_usr_local_bin_nomad + + - name: "Verify binary /usr/local/bin/nomad" + ansible.builtin.assert: + that: + - stat_usr_local_bin_nomad.stat.exists + - stat_usr_local_bin_nomad.stat.isreg + - stat_usr_local_bin_nomad.stat.pw_name == 'root' + - stat_usr_local_bin_nomad.stat.gr_name == 'root' + - stat_usr_local_bin_nomad.stat.mode == '0755' + + - name: "Test: directory /etc/nomad.d" + block: + - name: "Stat directory /etc/nomad.d" + ansible.builtin.stat: + path: "/etc/nomad.d" + register: stat_etc_nomad_d + + - name: "Stat file /etc/nomad.d/nomad.env" + ansible.builtin.stat: + path: "/etc/nomad.d/nomad.env" + register: stat_etc_nomad_d_nomad_env + + - name: "Stat file /etc/nomad.d/nomad.json" + ansible.builtin.stat: + path: "/etc/nomad.d/nomad.json" + register: stat_etc_nomad_d_nomad_json + + - name: "Slurp file /etc/nomad.d/nomad.json" + ansible.builtin.slurp: + src: "/etc/nomad.d/nomad.json" + register: slurp_etc_nomad_d_nomad_json + + - name: "Verify directory /etc/nomad.d" + ansible.builtin.assert: + that: + - stat_etc_nomad_d.stat.exists + - stat_etc_nomad_d.stat.isdir + - stat_etc_nomad_d.stat.pw_name == 'nomad' + - stat_etc_nomad_d.stat.gr_name == 'nomad' + - stat_etc_nomad_d.stat.mode == '0755' + - stat_etc_nomad_d_nomad_env.stat.exists + - stat_etc_nomad_d_nomad_env.stat.isreg + - stat_etc_nomad_d_nomad_env.stat.pw_name == 'nomad' + - stat_etc_nomad_d_nomad_env.stat.gr_name == 'nomad' + - stat_etc_nomad_d_nomad_env.stat.mode == '0600' + - stat_etc_nomad_d_nomad_json.stat.exists + - stat_etc_nomad_d_nomad_json.stat.isreg + - stat_etc_nomad_d_nomad_json.stat.pw_name == 'nomad' + - stat_etc_nomad_d_nomad_json.stat.gr_name == 'nomad' + - stat_etc_nomad_d_nomad_json.stat.mode == '0600' + - slurp_etc_nomad_d_nomad_json.content != '' + + - name: "Test: directory /opt/nomad" + block: + - name: "Stat directory /opt/nomad" + ansible.builtin.stat: + path: "/opt/nomad" + register: stat_opt_nomad + + - name: "Verify directory /opt/nomad" + ansible.builtin.assert: + that: + - stat_opt_nomad.stat.exists + - stat_opt_nomad.stat.isdir + - stat_opt_nomad.stat.pw_name == 'nomad' + - stat_opt_nomad.stat.gr_name == 'nomad' + - stat_opt_nomad.stat.mode == '0755' + + - name: "Test: service nomad" + block: + - name: "Get service nomad" + ansible.builtin.service_facts: + + - name: "Stat file /etc/systemd/system/nomad.service" + ansible.builtin.stat: + path: "/etc/systemd/system/nomad.service" + register: stat_etc_systemd_system_nomad_service + + - name: "Slurp file /etc/systemd/system/nomad.service" + ansible.builtin.slurp: + src: "/etc/systemd/system/nomad.service" + register: slurp_etc_systemd_system_nomad_service + + - name: "Verify service nomad" + ansible.builtin.assert: + that: + - stat_etc_systemd_system_nomad_service.stat.exists + - stat_etc_systemd_system_nomad_service.stat.isreg + - stat_etc_systemd_system_nomad_service.stat.pw_name == 'root' + - stat_etc_systemd_system_nomad_service.stat.gr_name == 'root' + - stat_etc_systemd_system_nomad_service.stat.mode == '0644' + - slurp_etc_systemd_system_nomad_service.content != '' + - ansible_facts.services['nomad.service'] is defined + - ansible_facts.services['nomad.service']['source'] == 'systemd' + - ansible_facts.services['nomad.service']['state'] == 'running' + - ansible_facts.services['nomad.service']['status'] == 'enabled' + + - name: "Test: interaction nomad" + block: + - name: "Command nomad var put" + ansible.builtin.command: "nomad var put secret/foobar foo=bar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + changed_when: false + register: nomad_var_put + + - name: "Command nomad var get" + ansible.builtin.command: "nomad var get secret/foobar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + changed_when: false + register: nomad_var_get + + - name: "Command nomad var purge" + ansible.builtin.command: "nomad var purge secret/foobar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + changed_when: false + register: nomad_var_purge + + - name: "Command nomad server members" + ansible.builtin.command: "nomad server members" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + changed_when: false + register: nomad_server_members + + - name: "Verify nomad interaction" + ansible.builtin.assert: + that: + - "'instance.global' in nomad_server_members.stdout" + - "'\"Items\": {\n \"foo\": \"bar\"\n }' in nomad_var_put.stdout" + - "'\"Items\": {\n \"foo\": \"bar\"\n }' in nomad_var_get.stdout" + - nomad_var_purge.stdout == 'Successfully purged variable \"secret/foobar\"!' diff --git a/molecule/nomad_with_acl_enabled/converge.yml b/molecule/nomad_with_acl_enabled/converge.yml new file mode 100644 index 0000000..ea6946e --- /dev/null +++ b/molecule/nomad_with_acl_enabled/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + become: true + tasks: + - name: "Include ednz_cloud.hashistack.nomad" + ansible.builtin.include_role: + name: "ednz_cloud.hashistack.nomad" diff --git a/molecule/nomad_with_acl_enabled/group_vars/all.yml b/molecule/nomad_with_acl_enabled/group_vars/all.yml new file mode 100644 index 0000000..627fe25 --- /dev/null +++ b/molecule/nomad_with_acl_enabled/group_vars/all.yml @@ -0,0 +1,12 @@ +--- +# defaults file for hashicorp_nomad + +##################### +# ACL configuration # +##################### + +nomad_acl_configuration: + enabled: true + token_ttl: 30s + policy_ttl: 60s + role_ttl: 60s diff --git a/molecule/nomad_with_acl_enabled/molecule.yml b/molecule/nomad_with_acl_enabled/molecule.yml new file mode 100644 index 0000000..e0b7ba7 --- /dev/null +++ b/molecule/nomad_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: nomad_with_acl_enabled + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/nomad_with_acl_enabled/prepare.yml b/molecule/nomad_with_acl_enabled/prepare.yml new file mode 100644 index 0000000..c216743 --- /dev/null +++ b/molecule/nomad_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/nomad_with_acl_enabled/requirements.yml b/molecule/nomad_with_acl_enabled/requirements.yml new file mode 100644 index 0000000..d3761e2 --- /dev/null +++ b/molecule/nomad_with_acl_enabled/requirements.yml @@ -0,0 +1,5 @@ +--- +# requirements file for molecule +roles: + - name: ednz_cloud.manage_repositories + - name: ednz_cloud.manage_apt_packages diff --git a/molecule/nomad_with_acl_enabled/verify.yml b/molecule/nomad_with_acl_enabled/verify.yml new file mode 100644 index 0000000..acce698 --- /dev/null +++ b/molecule/nomad_with_acl_enabled/verify.yml @@ -0,0 +1,185 @@ +--- +- name: Verify + hosts: all + gather_facts: true + become: true + tasks: + - name: "Test: nomad user and group" + block: + - name: "Getent user nomad" + ansible.builtin.getent: + database: passwd + key: nomad + register: nomad_user + + - name: "Getent group nomad" + ansible.builtin.getent: + database: group + key: nomad + register: nomad_group + + - name: "Verify nomad user and group" + ansible.builtin.assert: + that: + - not nomad_user.failed + - not nomad_group.failed + - "'nomad' in nomad_user.ansible_facts.getent_passwd.keys()" + - "'/home/nomad' in nomad_user.ansible_facts.getent_passwd['nomad']" + - "'/bin/false' in nomad_user.ansible_facts.getent_passwd['nomad']" + - "'nomad' in nomad_group.ansible_facts.getent_group.keys()" + + - name: "Test: binary /usr/local/bin/nomad" + block: + - name: "Stat binary /usr/local/bin/nomad" + ansible.builtin.stat: + path: "/usr/local/bin/nomad" + register: stat_usr_local_bin_nomad + + - name: "Verify binary /usr/local/bin/nomad" + ansible.builtin.assert: + that: + - stat_usr_local_bin_nomad.stat.exists + - stat_usr_local_bin_nomad.stat.isreg + - stat_usr_local_bin_nomad.stat.pw_name == 'root' + - stat_usr_local_bin_nomad.stat.gr_name == 'root' + - stat_usr_local_bin_nomad.stat.mode == '0755' + + - name: "Test: directory /etc/nomad.d" + block: + - name: "Stat directory /etc/nomad.d" + ansible.builtin.stat: + path: "/etc/nomad.d" + register: stat_etc_nomad_d + + - name: "Stat file /etc/nomad.d/nomad.env" + ansible.builtin.stat: + path: "/etc/nomad.d/nomad.env" + register: stat_etc_nomad_d_nomad_env + + - name: "Stat file /etc/nomad.d/nomad.json" + ansible.builtin.stat: + path: "/etc/nomad.d/nomad.json" + register: stat_etc_nomad_d_nomad_json + + - name: "Slurp file /etc/nomad.d/nomad.json" + ansible.builtin.slurp: + src: "/etc/nomad.d/nomad.json" + register: slurp_etc_nomad_d_nomad_json + + - name: "Verify directory /etc/nomad.d" + ansible.builtin.assert: + that: + - stat_etc_nomad_d.stat.exists + - stat_etc_nomad_d.stat.isdir + - stat_etc_nomad_d.stat.pw_name == 'nomad' + - stat_etc_nomad_d.stat.gr_name == 'nomad' + - stat_etc_nomad_d.stat.mode == '0755' + - stat_etc_nomad_d_nomad_env.stat.exists + - stat_etc_nomad_d_nomad_env.stat.isreg + - stat_etc_nomad_d_nomad_env.stat.pw_name == 'nomad' + - stat_etc_nomad_d_nomad_env.stat.gr_name == 'nomad' + - stat_etc_nomad_d_nomad_env.stat.mode == '0600' + - stat_etc_nomad_d_nomad_json.stat.exists + - stat_etc_nomad_d_nomad_json.stat.isreg + - stat_etc_nomad_d_nomad_json.stat.pw_name == 'nomad' + - stat_etc_nomad_d_nomad_json.stat.gr_name == 'nomad' + - stat_etc_nomad_d_nomad_json.stat.mode == '0600' + - slurp_etc_nomad_d_nomad_json.content != '' + + - name: "Test: directory /opt/nomad" + block: + - name: "Stat directory /opt/nomad" + ansible.builtin.stat: + path: "/opt/nomad" + register: stat_opt_nomad + + - name: "Verify directory /opt/nomad" + ansible.builtin.assert: + that: + - stat_opt_nomad.stat.exists + - stat_opt_nomad.stat.isdir + - stat_opt_nomad.stat.pw_name == 'nomad' + - stat_opt_nomad.stat.gr_name == 'nomad' + - stat_opt_nomad.stat.mode == '0755' + + - name: "Test: service nomad" + block: + - name: "Get service nomad" + ansible.builtin.service_facts: + + - name: "Stat file /etc/systemd/system/nomad.service" + ansible.builtin.stat: + path: "/etc/systemd/system/nomad.service" + register: stat_etc_systemd_system_nomad_service + + - name: "Slurp file /etc/systemd/system/nomad.service" + ansible.builtin.slurp: + src: "/etc/systemd/system/nomad.service" + register: slurp_etc_systemd_system_nomad_service + + - name: "Verify service nomad" + ansible.builtin.assert: + that: + - stat_etc_systemd_system_nomad_service.stat.exists + - stat_etc_systemd_system_nomad_service.stat.isreg + - stat_etc_systemd_system_nomad_service.stat.pw_name == 'root' + - stat_etc_systemd_system_nomad_service.stat.gr_name == 'root' + - stat_etc_systemd_system_nomad_service.stat.mode == '0644' + - slurp_etc_systemd_system_nomad_service.content != '' + - ansible_facts.services['nomad.service'] is defined + - ansible_facts.services['nomad.service']['source'] == 'systemd' + - ansible_facts.services['nomad.service']['state'] == 'running' + - ansible_facts.services['nomad.service']['status'] == 'enabled' + + - name: "Test: bootstrap acl nomad" + block: + - name: "Command nomad acl bootstrap" + ansible.builtin.command: "nomad acl bootstrap -json" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + changed_when: false + register: nomad_acl_bootstrap + + - name: "Test: interaction nomad" + vars: + acl_token: "{{ nomad_acl_bootstrap.stdout|from_json|json_query('SecretID') }}" + block: + - name: "Command nomad var put" + ansible.builtin.command: "nomad var put secret/foobar foo=bar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + NOMAD_TOKEN: "{{ acl_token }}" + changed_when: false + register: nomad_var_put + + - name: "Command nomad var get" + ansible.builtin.command: "nomad var get secret/foobar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + NOMAD_TOKEN: "{{ acl_token }}" + changed_when: false + register: nomad_var_get + + - name: "Command nomad var purge" + ansible.builtin.command: "nomad var purge secret/foobar" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + NOMAD_TOKEN: "{{ acl_token }}" + changed_when: false + register: nomad_var_purge + + - name: "Command nomad server members" + ansible.builtin.command: "nomad server members" + environment: + NOMAD_ADDR: "http://{{ ansible_default_ipv4.address }}:4646" + NOMAD_TOKEN: "{{ acl_token }}" + changed_when: false + register: nomad_server_members + + - name: "Verify nomad interaction" + ansible.builtin.assert: + that: + - "'instance.global' in nomad_server_members.stdout" + - "'\"Items\": {\n \"foo\": \"bar\"\n }' in nomad_var_put.stdout" + - "'\"Items\": {\n \"foo\": \"bar\"\n }' in nomad_var_get.stdout" + - nomad_var_purge.stdout == 'Successfully purged variable \"secret/foobar\"!' diff --git a/roles/nomad/defaults/main.yml b/roles/nomad/defaults/main.yml new file mode 100644 index 0000000..e5c2419 --- /dev/null +++ b/roles/nomad/defaults/main.yml @@ -0,0 +1,204 @@ +--- +# defaults file for hashicorp_nomad + +nomad_version: "latest" +nomad_start_service: true +nomad_config_dir: "/etc/nomad.d" +nomad_data_dir: "/opt/nomad" +nomad_certs_dir: "{{ nomad_config_dir }}/tls" +nomad_logs_dir: "/var/log/nomad" + +nomad_cni_plugins_install: true +nomad_cni_plugins_version: latest +nomad_cni_plugins_install_path: /opt/cni/bin + +nomad_extra_files: false +nomad_extra_files_list: [] + +nomad_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. + +nomad_extra_configuration: {} + +########### +# general # +########### + +nomad_region: global +nomad_datacenter: dc1 + +######################### +# address configuration # +######################### + +nomad_bind_addr: "0.0.0.0" +nomad_advertise_addr: "{{ ansible_default_ipv4.address }}" +nomad_address_configuration: + bind_addr: "{{ nomad_bind_addr }}" + addresses: + http: "{{ nomad_advertise_addr }}" + rpc: "{{ nomad_advertise_addr }}" + serf: "{{ nomad_advertise_addr }}" + advertise: + http: "{{ nomad_advertise_addr }}" + rpc: "{{ nomad_advertise_addr }}" + serf: "{{ nomad_advertise_addr }}" + ports: + http: 4646 + rpc: 4647 + serf: 4648 + +########################### +# autopilot configuration # +########################### + +nomad_autopilot_configuration: {} + +####################### +# leave configuration # +####################### + +nomad_leave_on_interrupt: false +nomad_leave_on_terminate: false + +######################## +# server configuration # +######################## + +nomad_enable_server: true +nomad_server_bootstrap_expect: 1 +nomad_server_configuration: + enabled: "{{ nomad_enable_server }}" + data_dir: "{{ nomad_data_dir }}/server" + encrypt: "{{ 'mysupersecretgossipencryptionkey'|b64encode }}" + server_join: + retry_join: + - "{{ ansible_default_ipv4.address }}" + +############################## +# client configuration # +############################## + +nomad_enable_client: false +nomad_client_configuration: + enabled: "{{ nomad_enable_client }}" + state_dir: "{{ nomad_data_dir }}/client" + cni_path: "{{ nomad_cni_plugins_install_path }}" # "/opt/cni/bin" + bridge_network_name: nomad + bridge_network_subnet: "172.26.64.0/20" + +#################### +# ui configuration # +#################### + +nomad_ui_configuration: + enabled: "{{ nomad_enable_server }}" + +######################### +# drivers configuration # +######################### + +nomad_driver_enable_docker: true +nomad_driver_enable_podman: false +nomad_driver_enable_raw_exec: false +nomad_driver_enable_java: false +nomad_driver_enable_qemu: false + +nomad_driver_configuration: + raw_exec: + enabled: false + +nomad_driver_extra_configuration: {} + +########### +# logging # +########### + +nomad_log_level: info +nomad_enable_log_to_file: false +nomad_log_to_file_configuration: + log_file: "{{ nomad_logs_dir }}/nomad.log" + log_rotate_duration: 24h + log_rotate_max_files: 30 + +##################### +# ACL configuration # +##################### + +nomad_acl_configuration: + enabled: false + token_ttl: 30s + policy_ttl: 60s + role_ttl: 60s + +################ +# internal tls # +################ + +nomad_enable_tls: false +nomad_tls_configuration: + http: true + rpc: true + ca_file: "/etc/ssl/certs/ca-certificates.crt" + cert_file: "{{ nomad_certs_dir }}/cert.pem" + key_file: "{{ nomad_certs_dir }}/key.pem" + verify_server_hostname: true + +nomad_certificates_extra_files_dir: + [] + # - src: "" + # dest: "{{ nomad_certs_dir }}" + +########################### +# telemetry configuration # +########################### + +nomad_telemetry_configuration: + collection_interval: 10s + disable_hostname: false + use_node_name: false + publish_allocation_metrics: false + publish_node_metrics: false + prefix_filter: [] + disable_dispatched_job_summary_metrics: false + prometheus_metrics: false + +###################### +# consul integration # +###################### + +nomad_enable_consul_integration: false +nomad_consul_integration_configuration: + address: "127.0.0.1:8500" + auto_advertise: true + ssl: false + token: "" + tags: [] + +nomad_consul_integration_tls_configuration: + ca_file: "/etc/ssl/certs/ca-certificates.crt" + +nomad_consul_integration_server_configuration: + server_auto_join: true + +nomad_consul_integration_client_configuration: + client_auto_join: true + grpc_address: "127.0.0.1:8502" + +nomad_consul_integration_client_tls_configuration: + grpc_ca_file: "/etc/ssl/certs/ca-certificates.crt" + +############################ +# nomad vault integration # +############################ + +nomad_enable_vault_integration: false +nomad_vault_integration_configuration: {} diff --git a/roles/nomad/handlers/main.yml b/roles/nomad/handlers/main.yml new file mode 100644 index 0000000..a4216c6 --- /dev/null +++ b/roles/nomad/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for hashicorp_nomad diff --git a/roles/nomad/meta/main.yml b/roles/nomad/meta/main.yml new file mode 100644 index 0000000..cc6aa7f --- /dev/null +++ b/roles/nomad/meta/main.yml @@ -0,0 +1,26 @@ +--- +# meta file for hashicorp_nomad +galaxy_info: + namespace: "ednz_cloud" + role_name: "hashicorp_nomad" + author: "Bertrand Lanson" + description: "Install and configure hashicorp nomad 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" + - "nomad" + +dependencies: [] diff --git a/roles/nomad/tasks/cni_install.yml b/roles/nomad/tasks/cni_install.yml new file mode 100644 index 0000000..cd11e26 --- /dev/null +++ b/roles/nomad/tasks/cni_install.yml @@ -0,0 +1,78 @@ +--- +# task/cni_install file for hashicorp_nomad +- name: "Nomad | Get release for cni_plugins:{{ nomad_cni_plugins_version }}" + vars: + _cni_plugins_url_ext: "{% if nomad_cni_plugins_version == 'latest'%}releases{% else %}releases/tags{% endif %}" + ansible.builtin.uri: + url: "{{ nomad_github_api }}/{{ nomad_cni_github_project }}/{{ _cni_plugins_url_ext }}/{{ nomad_cni_plugins_version }}" + return_content: true + register: _cni_plugins_new_release + +- name: "Nomad | Check if cni plugin is already installed" + ansible.builtin.stat: + path: "{{ nomad_cni_plugins_install_path }}/version" + changed_when: false + check_mode: false + register: _cni_plugins_is_installed + +- name: "Nomad | Check current cni plugin version" + ansible.builtin.command: "cat {{ nomad_cni_plugins_install_path }}/version" + changed_when: false + check_mode: false + register: _cni_plugins_old_release + when: _cni_plugins_is_installed.stat.exists + +- name: "Nomad | Set facts for wanted cni plugins release" + ansible.builtin.set_fact: + nomad_cni_plugins_wanted_version: "{{ _cni_plugins_new_release.json['tag_name']|regex_replace('v', '') }}" + when: _cni_plugins_new_release.json is defined + and (_cni_plugins_new_release.json | length > 0) + +- name: "Nomad | Set facts for current cni plugins release" + ansible.builtin.set_fact: + nomad_cni_plugins_current_version: "{{ _cni_plugins_old_release.stdout | regex_replace('v', '') }}" + when: _cni_plugins_old_release.stdout is defined + and (_cni_plugins_old_release.stdout | length > 0) + +- name: "Nomad | Create cni directory" + ansible.builtin.file: + path: "{{ nomad_cni_plugins_install_path }}" + state: directory + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + +- name: "Nomad | Install cni plugins" + when: nomad_cni_plugins_current_version is not defined + or nomad_cni_plugins_wanted_version not in nomad_cni_plugins_current_version + block: + - name: "Nomad | Install cni plugins version:{{ nomad_cni_plugins_version }}" + ansible.builtin.get_url: + url: "{{ nomad_github_url }}/{{ nomad_cni_github_project }}/releases/download/v{{ nomad_cni_plugins_wanted_version }}/cni-plugins-linux-{{ nomad_architecture }}-v{{ nomad_cni_plugins_wanted_version }}.tgz" + dest: "/tmp/cni_plugin.tgz" + mode: "0644" + register: _cni_plugins_download_archive + until: _cni_plugins_download_archive is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "Nomad | Unpack cni plugins" + ansible.builtin.unarchive: + src: "/tmp/cni_plugin.tgz" + dest: "{{ nomad_cni_plugins_install_path }}" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + remote_src: true + + - name: "Nomad | Remove temporary archive" + ansible.builtin.file: + path: "/tmp/cni_plugin.tgz" + state: absent + + - name: "Nomad | Update version file" + ansible.builtin.copy: + content: "{{ nomad_cni_plugins_wanted_version }}" + dest: "{{ nomad_cni_plugins_install_path }}/version" + mode: "0600" diff --git a/roles/nomad/tasks/configure.yml b/roles/nomad/tasks/configure.yml new file mode 100644 index 0000000..0de7a17 --- /dev/null +++ b/roles/nomad/tasks/configure.yml @@ -0,0 +1,74 @@ +--- +# task/configure file for hashicorp_nomad +- name: "Nomad | Create nomad.env" + ansible.builtin.template: + src: nomad.env.j2 + dest: "{{ nomad_config_dir }}/nomad.env" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0600" + register: _nomad_env_file + +- name: "Nomad | Copy nomad.json template" + ansible.builtin.template: + src: nomad.json.j2 + dest: "{{ nomad_config_dir }}/nomad.json" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0600" + register: _nomad_config_file + +- name: "Nomad | Set restart-check variable" + ansible.builtin.set_fact: + _nomad_service_need_restart: true + when: _nomad_env_file.changed or + _nomad_config_file.changed + +- name: "Nomad | Copy extra configuration files" + when: nomad_extra_files + block: + - name: "Nomad | Get extra file types" + ansible.builtin.stat: + path: "{{ item.src }}" + loop: "{{ nomad_extra_files_list }}" + register: nomad_extra_file_stat + delegate_to: localhost + + - name: "Nomad | Set list for file sources" + vars: + _nomad_file_sources: [] + ansible.builtin.set_fact: + _nomad_file_sources: "{{ _nomad_file_sources + [item.item] }}" + when: item.stat.isreg + loop: "{{ nomad_extra_file_stat.results }}" + loop_control: + loop_var: item + delegate_to: localhost + + - name: "Nomad | Set list for directory sources" + vars: + _nomad_dir_sources: [] + ansible.builtin.set_fact: + _nomad_dir_sources: "{{ _nomad_dir_sources + [item.item] }}" + when: item.stat.isdir + loop: "{{ nomad_extra_file_stat.results }}" + loop_control: + loop_var: item + delegate_to: localhost + + - name: "Nomad | Template extra file sources" + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest | regex_replace('\\.j2$', '') }}" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0700" + loop: "{{ _nomad_file_sources }}" + when: _nomad_file_sources is defined + + - name: "Nomad | Template extra directory sources" + ansible.builtin.include_tasks: recursive_copy_extra_dirs.yml + loop: "{{ _nomad_dir_sources }}" + loop_control: + loop_var: dir_source_item + when: _nomad_dir_sources is defined diff --git a/roles/nomad/tasks/install.yml b/roles/nomad/tasks/install.yml new file mode 100644 index 0000000..3ac3a5a --- /dev/null +++ b/roles/nomad/tasks/install.yml @@ -0,0 +1,140 @@ +--- +- name: "Nomad | Get latest release of nomad" + when: nomad_version == 'latest' + block: + - name: "Nomad | Get latest nomad release from github api" + ansible.builtin.uri: + url: "{{ nomad_github_api }}/hashicorp/nomad/releases/latest" + return_content: true + register: _nomad_latest_release + + - name: "Nomad | Set wanted nomad version to latest tag" + ansible.builtin.set_fact: + _nomad_wanted_version: "{{ _nomad_latest_release.json['tag_name']|regex_replace('v', '') }}" + +- name: "Nomad | Set wanted nomad version to {{ nomad_version }}" + ansible.builtin.set_fact: + _nomad_wanted_version: "{{ nomad_version|regex_replace('v', '') }}" + when: nomad_version != 'latest' + +- name: "Nomad | Get current nomad version" + block: + - name: "Nomad | Stat nomad version file" + ansible.builtin.stat: + path: "{{ nomad_config_dir }}/.version" + changed_when: false + check_mode: false + register: _nomad_version_file + + - name: "Nomad | Get current nomad version" + ansible.builtin.slurp: + src: "{{ _nomad_version_file.stat.path }}" + when: + - _nomad_version_file.stat.exists + - _nomad_version_file.stat.isreg + register: _nomad_current_version + +- name: "Nomad | Download and install nomad binary" + when: _nomad_current_version is not defined + or _nomad_wanted_version != (_nomad_current_version.content|default('')|b64decode) + block: + - name: "Nomad | Set nomad package name to download" + ansible.builtin.set_fact: + _nomad_package_name: >- + nomad_{{ _nomad_wanted_version }}_linux_{{ nomad_deb_architecture_map[ansible_architecture] }}.zip + _nomad_shasum_file_name: >- + nomad_{{ _nomad_wanted_version }}_SHA256SUMS + + - name: "Nomad | Download checksum file for nomad archive" + ansible.builtin.get_url: + url: "{{ nomad_repository_url }}/{{ _nomad_wanted_version }}/{{ _nomad_shasum_file_name }}" + dest: "/tmp/{{ _nomad_shasum_file_name }}" + mode: "0644" + register: _nomad_checksum_file + until: _nomad_checksum_file is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "Nomad | Extract correct checksum from checksum file" + ansible.builtin.command: + cmd: 'grep "{{ _nomad_package_name }}" /tmp/{{ _nomad_shasum_file_name }}' + changed_when: false + register: _nomad_expected_checksum_line + + - name: "Nomad | Parse the expected checksum" + ansible.builtin.set_fact: + _nomad_expected_checksum: "{{ _nomad_expected_checksum_line.stdout.split()[0] }}" + + - name: "Nomad | Download nomad binary archive" + ansible.builtin.get_url: + url: "{{ nomad_repository_url }}/{{ _nomad_wanted_version }}/{{ _nomad_package_name }}" + dest: "/tmp/{{ _nomad_package_name }}" + mode: "0644" + checksum: "sha256:{{ _nomad_expected_checksum }}" + register: _nomad_binary_archive + until: _nomad_binary_archive is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "Nomad | Create temporary directory for archive decompression" + ansible.builtin.file: + path: /tmp/nomad + state: directory + mode: "0755" + + - name: "Nomad | Unpack nomad archive" + ansible.builtin.unarchive: + src: "/tmp/{{ _nomad_package_name }}" + dest: "/tmp/nomad" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + remote_src: true + + - name: "Nomad | Copy nomad binary to {{ nomad_binary_path }}" + ansible.builtin.copy: + src: /tmp/nomad/nomad + dest: "{{ nomad_binary_path }}" + owner: root + group: root + mode: "0755" + remote_src: true + force: true + + - name: "Nomad | Update nomad version file" + ansible.builtin.copy: + content: "{{ _nomad_wanted_version }}" + dest: "{{ nomad_config_dir }}/.version" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0600" + + - name: "Nomad | Set restart-check variable" + ansible.builtin.set_fact: + _nomad_service_need_restart: true + + - name: "Nomad | Cleanup temporary directory" + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /tmp/nomad + - /tmp/{{ _nomad_package_name }} + - /tmp/{{ _nomad_shasum_file_name }} + +- name: "Nomad | Copy systemd service file for nomad" + ansible.builtin.template: + src: "nomad.service.j2" + dest: "/etc/systemd/system/nomad.service" + owner: root + group: root + mode: "0644" + register: _nomad_unit_file + +- name: "Nomad | Set reload-check & restart-check variable" + ansible.builtin.set_fact: + _nomad_service_need_reload: true + _nomad_service_need_restart: true + when: _nomad_unit_file.changed # noqa: no-handler diff --git a/roles/nomad/tasks/main.yml b/roles/nomad/tasks/main.yml new file mode 100644 index 0000000..a3ca21c --- /dev/null +++ b/roles/nomad/tasks/main.yml @@ -0,0 +1,47 @@ +--- +# task/main file for hashicorp_nomad +- name: "Nomad | Set reload-check & restart-check variable" + ansible.builtin.set_fact: + _nomad_service_need_reload: false + _nomad_service_need_restart: false + +- name: "Nomad | Import merge_variables.yml" + ansible.builtin.include_tasks: merge_variables.yml + +- name: "Nomad | Import prerequisites.yml" + ansible.builtin.include_tasks: prerequisites.yml + +- name: "Nomad | Import install.yml" + ansible.builtin.include_tasks: install.yml + +- name: "Nomad | Import cni_install.yml" + ansible.builtin.include_tasks: cni_install.yml + when: nomad_cni_plugins_install + +- name: "Nomad | Import configure.yml" + ansible.builtin.include_tasks: configure.yml + +- name: "Nomad | Populate service facts" + ansible.builtin.service_facts: + +- name: "Nomad | Set restart-check variable" + ansible.builtin.set_fact: + _nomad_service_need_restart: true + when: ansible_facts.services[nomad_service_name~'.service'].state != 'running' + +- name: "Nomad | Enable service: {{ nomad_service_name }}" + ansible.builtin.service: + name: "{{ nomad_service_name }}" + enabled: true + +- name: "Nomad | Reload systemd daemon" + ansible.builtin.systemd: + daemon_reload: true + when: _nomad_service_need_reload + +- name: "Nomad | Start service: {{ nomad_service_name }}" + ansible.builtin.service: + name: "{{ nomad_service_name }}" + state: restarted + throttle: 1 + when: _nomad_service_need_restart diff --git a/roles/nomad/tasks/merge_variables.yml b/roles/nomad/tasks/merge_variables.yml new file mode 100644 index 0000000..b59a9fc --- /dev/null +++ b/roles/nomad/tasks/merge_variables.yml @@ -0,0 +1,150 @@ +--- +# task/merge_variables file for hashicorp_nomad +- name: "Nomad | Merge stringified configuration" + vars: + _config_to_merge: "{{ nomad_configuration_string }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge|from_yaml, recursive=true) + }}" + +- name: "Nomad | Merge addresses configuration" + vars: + _config_to_merge: "{{ nomad_address_configuration }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Nomad | Merge consul integration configuration" + when: + - nomad_enable_consul_integration | bool + block: + - name: "Nomad | Merge consul tls configuration" + when: + - nomad_consul_integration_configuration.ssl is defined + - nomad_consul_integration_configuration.ssl | bool + block: + - name: "Nomad | Merge consul default client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_tls_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul configuration for nomad servers" + when: + - nomad_enable_server + block: + - name: "Nomad | Merge consul default server configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_server_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul configuration for nomad clients" + when: + - nomad_enable_client + block: + - name: "Nomad | Merge consul default client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_client_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Merge consul tls client configuration" + vars: + _config_to_merge: "{{ nomad_consul_integration_client_tls_configuration }}" + ansible.builtin.set_fact: + nomad_consul_integration_configuration: "{{ + nomad_consul_integration_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: + - nomad_consul_integration_configuration.ssl is defined + - nomad_consul_integration_configuration.ssl | bool + + - name: "Nomad | Merge consul block into main configuration" + vars: + _config_to_merge: + consul: "{{ nomad_consul_integration_configuration }}" + ansible.builtin.set_fact: + hashicorp_nomad_configuration: "{{ + hashicorp_nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Nomad | Merge TLS configuration" + when: nomad_enable_tls | bool + block: + - name: "Nomad | Merge TLS configuration" + vars: + _config_to_merge: + tls: "{{ nomad_tls_configuration }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + + - name: "Nomad | Add certificates directory to extra_files_dir" + ansible.builtin.set_fact: + nomad_extra_files_list: "{{ + nomad_extra_files_list + + nomad_certificates_extra_files_dir + | unique + | sort + }}" + +- name: "Nomad | Merge plugin configuration" + vars: + _config_to_merge: + plugin: "{{ + nomad_driver_configuration | + combine(nomad_driver_extra_configuration, recursive=true) + }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: nomad_enable_client | bool + +- name: "Nomad | Merge extra configuration settings" + vars: + _config_to_merge: "{{ nomad_extra_configuration }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + +- name: "Nomad | Merge log to file configuration" + vars: + _config_to_merge: "{{ nomad_log_to_file_configuration }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" + when: nomad_enable_log_to_file + +- name: "Nomad | Merge telemetry configuration" + vars: + _config_to_merge: + telemetry: "{{ nomad_telemetry_configuration }}" + ansible.builtin.set_fact: + nomad_configuration: "{{ + nomad_configuration | + combine(_config_to_merge, recursive=true) + }}" diff --git a/roles/nomad/tasks/prerequisites.yml b/roles/nomad/tasks/prerequisites.yml new file mode 100644 index 0000000..3928cf5 --- /dev/null +++ b/roles/nomad/tasks/prerequisites.yml @@ -0,0 +1,46 @@ +--- +# task/prerequisites file for hashicorp_nomad +- name: "Nomad | Create group {{ nomad_group }}" + ansible.builtin.group: + name: "{{ nomad_user }}" + state: present + +- name: "Nomad | Create user {{ nomad_user }}" + ansible.builtin.user: + name: "{{ nomad_user }}" + group: "{{ nomad_group }}" + shell: /bin/false + state: present + +- name: "Nomad | Create directory {{ nomad_config_dir }}" + ansible.builtin.file: + path: "{{ nomad_config_dir }}" + state: directory + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + +- name: "Nomad | Create directory {{ nomad_data_dir }}" + ansible.builtin.file: + path: "{{ nomad_data_dir }}" + state: directory + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + +- name: "Nomad | Create directory {{ nomad_certs_dir }}" + ansible.builtin.file: + path: "{{ nomad_certs_dir }}" + state: directory + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + +- name: "Nomad | Create directory {{ nomad_logs_dir }}" + ansible.builtin.file: + path: "{{ nomad_logs_dir }}" + state: directory + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0755" + when: nomad_enable_log_to_file diff --git a/roles/nomad/tasks/recursive_copy_extra_dirs.yml b/roles/nomad/tasks/recursive_copy_extra_dirs.yml new file mode 100644 index 0000000..2424d9e --- /dev/null +++ b/roles/nomad/tasks/recursive_copy_extra_dirs.yml @@ -0,0 +1,27 @@ +--- +# task/recursive_copy_extra_dirs file for hashicorp_nomad +- name: "Nomad | Ensure destination directory exists" + ansible.builtin.file: + path: "{{ dir_source_item.dest }}" + recurse: true + state: directory + mode: "0775" + +- name: "Nomad | 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: "Nomad | Template extra directory sources" + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ dir_source_item.dest }}/{{ item.path | regex_replace('\\.j2$', '') }}" + owner: "{{ nomad_user }}" + group: "{{ nomad_group }}" + mode: "0700" + with_community.general.filetree: "{{ dir_source_item.src }}/" + when: item.state == 'file' diff --git a/roles/nomad/templates/nomad.env.j2 b/roles/nomad/templates/nomad.env.j2 new file mode 100644 index 0000000..67798c9 --- /dev/null +++ b/roles/nomad/templates/nomad.env.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} +{% for item in nomad_env_variables %} +{{ item }}="{{ nomad_env_variables[item] }}" +{% endfor %} diff --git a/roles/nomad/templates/nomad.json.j2 b/roles/nomad/templates/nomad.json.j2 new file mode 100644 index 0000000..c0bf3da --- /dev/null +++ b/roles/nomad/templates/nomad.json.j2 @@ -0,0 +1 @@ +{{ nomad_configuration|to_nice_json }} diff --git a/roles/nomad/templates/nomad.service.j2 b/roles/nomad/templates/nomad.service.j2 new file mode 100644 index 0000000..e5a1570 --- /dev/null +++ b/roles/nomad/templates/nomad.service.j2 @@ -0,0 +1,33 @@ +[Unit] +Description=Nomad +Documentation=https://developer.hashicorp.com/nomad/docs +Wants=network-online.target +After=network-online.target +ConditionFileNotEmpty={{ nomad_config_dir }}/nomad.json +{% if nomad_configuration.consul.address is defined %} +Wants=consul.service +After=consul.service +{% endif %} + +[Service] +EnvironmentFile=-{{ nomad_config_dir }}/nomad.env +{% if not (nomad_configuration.client.enabled is defined and nomad_configuration.client.enabled) %} +User={{ nomad_user }} +Group={{ nomad_group }} +{% else %} +User=root +Group=root +{% endif %} +ExecStart={{ nomad_binary_path }} agent -config {{ nomad_config_dir }}/nomad.json +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +KillSignal=SIGINT +Restart=on-failure +LimitNOFILE=65536 +LimitNPROC=infinity +RestartSec=2 +TasksMax=infinity +OOMScoreAdjust=-1000 + +[Install] +WantedBy=multi-user.target diff --git a/roles/nomad/vars/main.yml b/roles/nomad/vars/main.yml new file mode 100644 index 0000000..6222923 --- /dev/null +++ b/roles/nomad/vars/main.yml @@ -0,0 +1,33 @@ +--- +# vars file for hashicorp_nomad +nomad_user: nomad +nomad_group: nomad +nomad_binary_path: /usr/local/bin/nomad +nomad_deb_architecture_map: + x86_64: "amd64" + aarch64: "arm64" + armv7l: "arm" + armv6l: "arm" +nomad_architecture: "{{ nomad_deb_architecture_map[ansible_architecture] | default(ansible_architecture) }}" +nomad_service_name: "nomad" +nomad_github_api: https://api.github.com/repos +nomad_cni_github_project: containernetworking/plugins +nomad_github_project: hashicorp/nomad +nomad_github_url: https://github.com +nomad_repository_url: https://releases.hashicorp.com/nomad + +nomad_configuration: + datacenter: "{{ nomad_datacenter }}" + region: "{{ nomad_region }}" + data_dir: "{{ nomad_data_dir }}" + leave_on_interrupt: "{{ nomad_leave_on_interrupt }}" + leave_on_terminate: "{{ nomad_leave_on_terminate }}" + acl: "{{ nomad_acl_configuration }}" + server: "{{ nomad_server_configuration }}" + client: "{{ nomad_client_configuration }}" + ui: "{{ nomad_ui_configuration }}" + log_level: "{{ nomad_log_level }}" + +nomad_configuration_string: | + server: + bootstrap_expect: {{ nomad_server_bootstrap_expect }}