From 09d030474898f3ca8e3e0d3d2a0ad2097d673e71 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Mon, 22 Jul 2024 22:26:24 +0200 Subject: [PATCH] feat(cni): add specialized role ton install cni plugins --- molecule/cni_default/converge.yml | 8 ++ molecule/cni_default/molecule.yml | 37 ++++++ molecule/cni_default/prepare.yml | 13 ++ molecule/cni_default/requirements.yml | 4 + molecule/cni_default/verify.yml | 170 +++++++++++++++++++++++++ roles/cni/defaults/main.yml | 7 + roles/cni/handlers/main.yml | 2 + roles/cni/meta/main.yml | 1 + roles/cni/tasks/cni_install.yml | 70 ++++++++++ roles/cni/tasks/consul_cni_install.yml | 114 +++++++++++++++++ roles/cni/tasks/main.yml | 10 ++ roles/cni/tasks/prerequisites.yml | 21 +++ roles/cni/vars/main.yml | 11 ++ 13 files changed, 468 insertions(+) create mode 100644 molecule/cni_default/converge.yml create mode 100644 molecule/cni_default/molecule.yml create mode 100644 molecule/cni_default/prepare.yml create mode 100644 molecule/cni_default/requirements.yml create mode 100644 molecule/cni_default/verify.yml create mode 100644 roles/cni/defaults/main.yml create mode 100644 roles/cni/handlers/main.yml create mode 100644 roles/cni/meta/main.yml create mode 100644 roles/cni/tasks/cni_install.yml create mode 100644 roles/cni/tasks/consul_cni_install.yml create mode 100644 roles/cni/tasks/main.yml create mode 100644 roles/cni/tasks/prerequisites.yml create mode 100644 roles/cni/vars/main.yml diff --git a/molecule/cni_default/converge.yml b/molecule/cni_default/converge.yml new file mode 100644 index 0000000..5fc2cb1 --- /dev/null +++ b/molecule/cni_default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + become: true + tasks: + - name: "Include ednz_cloud.hashistack.cni" + ansible.builtin.include_role: + name: "ednz_cloud.hashistack.cni" diff --git a/molecule/cni_default/molecule.yml b/molecule/cni_default/molecule.yml new file mode 100644 index 0000000..6482b97 --- /dev/null +++ b/molecule/cni_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: cni_default + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - cleanup + - destroy diff --git a/molecule/cni_default/prepare.yml b/molecule/cni_default/prepare.yml new file mode 100644 index 0000000..c216743 --- /dev/null +++ b/molecule/cni_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/cni_default/requirements.yml b/molecule/cni_default/requirements.yml new file mode 100644 index 0000000..329e789 --- /dev/null +++ b/molecule/cni_default/requirements.yml @@ -0,0 +1,4 @@ +--- +# requirements file for molecule +roles: + - name: ednz_cloud.manage_apt_packages diff --git a/molecule/cni_default/verify.yml b/molecule/cni_default/verify.yml new file mode 100644 index 0000000..db7c5d1 --- /dev/null +++ b/molecule/cni_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/roles/cni/defaults/main.yml b/roles/cni/defaults/main.yml new file mode 100644 index 0000000..5605796 --- /dev/null +++ b/roles/cni/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# defaults file for cni +cni_plugins_version: "latest" +cni_plugins_install_path: /opt/cni/bin +cni_plugins_install_consul_cni: false +cni_user: nomad +cni_group: nomad diff --git a/roles/cni/handlers/main.yml b/roles/cni/handlers/main.yml new file mode 100644 index 0000000..c921b72 --- /dev/null +++ b/roles/cni/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for cni diff --git a/roles/cni/meta/main.yml b/roles/cni/meta/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/cni/meta/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/cni/tasks/cni_install.yml b/roles/cni/tasks/cni_install.yml new file mode 100644 index 0000000..e897d6a --- /dev/null +++ b/roles/cni/tasks/cni_install.yml @@ -0,0 +1,70 @@ +--- +# task/cni_install file for cni +- name: "CNI Plugins | Get release for cni_plugins:{{ cni_plugins_version }}" + vars: + _cni_plugins_url_ext: "{% if cni_plugins_version == 'latest'%}releases{% else %}releases/tags{% endif %}" + ansible.builtin.uri: + url: "{{ cni_github_api }}/{{ cni_github_project }}/{{ _cni_plugins_url_ext }}/{{ cni_plugins_version }}" + return_content: true + register: _cni_plugins_latest_release + +- name: "CNI Plugins | Check if cni plugin is already installed" + ansible.builtin.stat: + path: "{{ cni_plugins_install_path }}/.version" + changed_when: false + check_mode: false + register: _cni_plugins_is_installed + +- name: "CNI Plugins | Check current cni plugin version" + ansible.builtin.command: "cat {{ cni_plugins_install_path }}/.version" + changed_when: false + check_mode: false + register: _cni_plugins_old_release + when: _cni_plugins_is_installed.stat.exists + +- name: "CNI Plugins | Set facts for wanted cni plugins release" + ansible.builtin.set_fact: + cni_plugins_wanted_version: "{{ _cni_plugins_latest_release.json['tag_name']|regex_replace('v', '') }}" + when: _cni_plugins_latest_release.json is defined + and (_cni_plugins_latest_release.json | length > 0) + +- name: "CNI Plugins | Set facts for current cni plugins release" + ansible.builtin.set_fact: + 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: "CNI Plugins | Install cni plugins" + when: cni_plugins_current_version is not defined + or cni_plugins_wanted_version not in cni_plugins_current_version + block: + - name: "CNI Plugins | Install cni plugins version:{{ cni_plugins_version }}" + ansible.builtin.get_url: + url: "{{ cni_github_url }}/{{ cni_github_project }}/releases/download/v{{ cni_plugins_wanted_version }}/cni-plugins-linux-{{ cni_architecture }}-v{{ 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: "CNI Plugins | Unpack cni plugins" + ansible.builtin.unarchive: + src: "/tmp/cni_plugin.tgz" + dest: "{{ cni_plugins_install_path }}" + owner: "{{ cni_user }}" + group: "{{ cni_group }}" + mode: "0755" + remote_src: true + + - name: "CNI Plugins | Remove temporary archive" + ansible.builtin.file: + path: "/tmp/cni_plugin.tgz" + state: absent + + - name: "CNI Plugins | Update version file" + ansible.builtin.copy: + content: "{{ cni_plugins_wanted_version }}" + dest: "{{ cni_plugins_install_path }}/.version" + mode: "0600" diff --git a/roles/cni/tasks/consul_cni_install.yml b/roles/cni/tasks/consul_cni_install.yml new file mode 100644 index 0000000..a04d9ac --- /dev/null +++ b/roles/cni/tasks/consul_cni_install.yml @@ -0,0 +1,114 @@ +--- +# task/consul_cni_install file for cni +- name: "CNI Plugins | Set wanted consul-cni version to latest tag" + ansible.builtin.set_fact: + _cni_consul_cni_wanted_version: "{{ _cni_plugins_latest_release.json['tag_name']|regex_replace('v', '') }}" + when: cni_plugins_version == 'latest' + +- name: "CNI Plugins | Set wanted consul-cni version to {{ cni_plugins_version }}" + ansible.builtin.set_fact: + _cni_consul_cni_wanted_version: "{{ cni_plugins_version|regex_replace('v', '') }}" + when: cni_plugins_version != 'latest' + +- name: "CNI Plugins | Get current consul-cni version" + block: + - name: "CNI Plugins | Stat consul-cni version file" + ansible.builtin.stat: + path: "{{ cni_plugins_install_path }}/.consul-cni-version" + changed_when: false + check_mode: false + register: _cni_consul_cni_version_file + + - name: "CNI Plugins | Get current consul-cni version" + ansible.builtin.slurp: + src: "{{ _cni_consul_cni_version_file.stat.path }}" + when: + - _cni_consul_cni_version_file.stat.exists + - _cni_consul_cni_version_file.stat.isreg + register: _cni_consul_cni_current_version + +- name: "CNI Plugins | Download and install consul-cni binary" + when: _cni_consul_cni_current_version is not defined + or _cni_consul_cni_wanted_version != (_cni_consul_cni_current_version.content|default('')|b64decode) + block: + - name: "CNI Plugins | Set consul-cni package name to download" + ansible.builtin.set_fact: + _cni_consul_cni_package_name: >- + consul-cni_{{ _cni_consul_cni_wanted_version }}_linux_{{ cni_deb_architecture_map[ansible_architecture] }}.zip + _cni_consul_cni_shasum_file_name: >- + consul-cni_{{ _cni_consul_cni_wanted_version }}_SHA256SUMS + + - name: "CNI Plugins | Download checksum file for consul-cni archive" + ansible.builtin.get_url: + url: "{{ cni_consul_cni_repository_url }}/{{ _cni_consul_cni_wanted_version }}/{{ _cni_consul_cni_shasum_file_name }}" + dest: "/tmp/{{ _cni_consul_cni_shasum_file_name }}" + mode: "0644" + register: _cni_consul_cni_checksum_file + until: _cni_consul_cni_checksum_file is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "CNI Plugins | Extract correct checksum from checksum file" + ansible.builtin.command: + cmd: 'grep "{{ _cni_consul_cni_package_name }}" /tmp/{{ _cni_consul_cni_shasum_file_name }}' + changed_when: false + register: _cni_consul_cni_expected_checksum_line + + - name: "CNI Plugins | Parse the expected checksum" + ansible.builtin.set_fact: + _cni_consul_cni_expected_checksum: "{{ _cni_consul_cni_expected_checksum_line.stdout.split()[0] }}" + + - name: "CNI Plugins | Download consul-cni binary archive" + ansible.builtin.get_url: + url: "{{ cni_consul_cni_repository_url }}/{{ _cni_consul_cni_wanted_version }}/{{ _cni_consul_cni_package_name }}" + dest: "/tmp/{{ _cni_consul_cni_package_name }}" + mode: "0644" + checksum: "sha256:{{ _cni_consul_cni_expected_checksum }}" + register: _cni_consul_cni_binary_archive + until: _cni_consul_cni_binary_archive is succeeded + retries: 5 + delay: 2 + check_mode: false + + - name: "CNI Plugins | Create temporary directory for archive decompression" + ansible.builtin.file: + path: /tmp/consul-cni + state: directory + mode: "0755" + + - name: "CNI Plugins | Unpack consul-cni archive" + ansible.builtin.unarchive: + src: "/tmp/{{ _cni_consul_cni_package_name }}" + dest: "/tmp/consul-cni" + owner: "{{ cni_user }}" + group: "{{ cni_group }}" + mode: "0755" + remote_src: true + + - name: "CNI Plugins | Copy consul-cni binary to {{ cni_plugins_install_path }}" + ansible.builtin.copy: + src: /tmp/consul-cni/consul-cni + dest: "{{ cni_plugins_install_path }}" + owner: "{{ cni_user }}" + group: "{{ cni_user }}" + mode: "0755" + remote_src: true + force: true + + - name: "CNI Plugins | Update consul-cni version file" + ansible.builtin.copy: + content: "{{ _cni_consul_cni_wanted_version }}" + dest: "{{ cni_plugins_install_path }}/.consul-cni-version" + owner: "{{ cni_user }}" + group: "{{ cni_group }}" + mode: "0600" + + - name: "CNI Plugins | Cleanup temporary directory" + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /tmp/consul-cni + - /tmp/{{ _cni_consul_cni_package_name }} + - /tmp/{{ _cni_consul_cni_shasum_file_name }} diff --git a/roles/cni/tasks/main.yml b/roles/cni/tasks/main.yml new file mode 100644 index 0000000..c85867f --- /dev/null +++ b/roles/cni/tasks/main.yml @@ -0,0 +1,10 @@ +--- +# task/main file for cni +- name: "CNI Plugins | Import prerequisites.yml" + ansible.builtin.include_tasks: prerequisites.yml + +- name: "CNI Plugins | Import cni_install.yml" + ansible.builtin.include_tasks: cni_install.yml + +- name: "CNI Plugins | Import consul_cni_install.yml" + ansible.builtin.include_tasks: consul_cni_install.yml diff --git a/roles/cni/tasks/prerequisites.yml b/roles/cni/tasks/prerequisites.yml new file mode 100644 index 0000000..24e6367 --- /dev/null +++ b/roles/cni/tasks/prerequisites.yml @@ -0,0 +1,21 @@ +--- +# task/prerequisites file for cni +- name: "CNI Plugins | Create group {{ cni_group }}" + ansible.builtin.group: + name: "{{ cni_user }}" + state: present + +- name: "CNI Plugins | Create user {{ cni_user }}" + ansible.builtin.user: + name: "{{ cni_user }}" + group: "{{ cni_group }}" + shell: /bin/false + state: present + +- name: "CNI Plugins | Create directory {{ cni_plugins_install_path }}" + ansible.builtin.file: + path: "{{ cni_plugins_install_path }}" + state: directory + owner: "{{ cni_user }}" + group: "{{ cni_group }}" + mode: "0755" diff --git a/roles/cni/vars/main.yml b/roles/cni/vars/main.yml new file mode 100644 index 0000000..e6949d8 --- /dev/null +++ b/roles/cni/vars/main.yml @@ -0,0 +1,11 @@ +--- +cni_github_api: https://api.github.com/repos +cni_github_project: containernetworking/plugins +cni_github_url: https://github.com +cni_deb_architecture_map: + x86_64: "amd64" + aarch64: "arm64" + armv7l: "arm" + armv6l: "arm" +cni_architecture: "{{ cni_deb_architecture_map[ansible_architecture] | default(ansible_architecture) }}" +cni_consul_cni_repository_url: https://releases.hashicorp.com/consul-cni