diff --git a/alts/shared/constants.py b/alts/shared/constants.py index 469311d6..8a7bfc32 100644 --- a/alts/shared/constants.py +++ b/alts/shared/constants.py @@ -1,6 +1,7 @@ from enum import IntEnum __all__ = [ + 'ALPINE_FLAVORS', 'API_VERSION', 'ARCHITECTURES', 'ALLOWED_CHANNELS', @@ -39,13 +40,14 @@ 'ppc64le', ] SUPPORTED_ARCHITECTURES = X32_ARCHITECTURES + X64_ARCHITECTURES + ['s390x'] -SUPPORTED_DISTRIBUTIONS = ['almalinux', 'centos', 'ubuntu', 'debian'] +SUPPORTED_DISTRIBUTIONS = ['almalinux', 'centos', 'ubuntu', 'debian', 'alpine'] RHEL_FLAVORS = [ 'rhel', 'fedora', 'centos', 'almalinux', ] +ALPINE_FLAVORS = ['alpine'] DEBIAN_FLAVORS = ['debian', 'ubuntu', 'raspbian'] ALLOWED_CHANNELS = ['stable', 'beta'] diff --git a/alts/shared/models.py b/alts/shared/models.py index 9a72ed03..05d7c5f2 100644 --- a/alts/shared/models.py +++ b/alts/shared/models.py @@ -290,6 +290,7 @@ def __init__(self, **data): # Supported architectures and distributions supported_architectures: List[str] = constants.SUPPORTED_ARCHITECTURES rhel_flavors: List[str] = constants.RHEL_FLAVORS + alpine_flavors: List[str] = constants.ALPINE_FLAVORS debian_flavors: List[str] = constants.DEBIAN_FLAVORS supported_runners: Union[List[str], str] = 'all' allowed_channel_names: List[str] = constants.ALLOWED_CHANNELS @@ -356,7 +357,7 @@ def broker_url(self) -> str: @computed_field(return_type=Set[str]) @property def supported_distributions(self): - return set(self.rhel_flavors + self.debian_flavors) + return set(self.rhel_flavors + self.debian_flavors + self.alpine_flavors) def get_celery_config_dict(self) -> Dict[str, Any]: config_dict = { diff --git a/alts/worker/runners/base.py b/alts/worker/runners/base.py index 5f4a1b23..491e4600 100644 --- a/alts/worker/runners/base.py +++ b/alts/worker/runners/base.py @@ -311,6 +311,8 @@ def pkg_manager(self): return 'yum' if self._dist_name in CONFIG.debian_flavors: return 'apt-get' + if self._dist_name in CONFIG.alpine_flavors: + return 'apk' raise ValueError(f'Unknown distribution: {self._dist_name}') @property @@ -786,6 +788,9 @@ def get_system_info_commands_list(self) -> Dict[str, tuple]: 'find', '/etc/yum.repos.d/', '-type', 'f', '-exec', 'cat', '{}', '+' ) + elif self._dist_name in CONFIG.alpine_flavors: + basic_commands['Installed packages'] = ('apk', '--allow-untrusted', 'info', '-vv') + basic_commands['Repositories list'] = ('cat', '/etc/apk/repositories') else: basic_commands['Installed packages'] = ('dpkg', '-l') basic_commands['Repositories list'] = ('apt-cache', 'policy') @@ -1175,6 +1180,8 @@ def check_package_existence( elif self.dist_name in CONFIG.debian_flavors: cmd = ('dpkg-query', '-Wf', r'${db:Status-Status} ${Package}\n', package_name) + elif self.dist_name in CONFIG.alpine_flavors: + cmd = ('apk', '--allow-untrusted', 'info', '-e', package_name) else: raise ValueError(f'Unknown distribution: {self.dist_name}') exit_code, stdout, stderr = self.exec_command(*cmd) diff --git a/alts/worker/runners/docker.py b/alts/worker/runners/docker.py index 0cb20816..adf6c889 100644 --- a/alts/worker/runners/docker.py +++ b/alts/worker/runners/docker.py @@ -259,6 +259,9 @@ def initial_provision(self, verbose=False): 'find', '/etc/yum.repos.d', '-type', 'f', '-exec', 'rm', '-f', '{}', '+', ) + if self.dist_name in CONFIG.alpine_flavors: + self._logger.info('Installing python3 apk package for alpine...') + self.exec_command('apk', '--allow-untrusted', 'add', 'python3') return super().initial_provision(verbose=verbose) @command_decorator( diff --git a/resources/roles/install_uninstall/tasks/main.yml b/resources/roles/install_uninstall/tasks/main.yml index 2aa4ea54..83be9d06 100644 --- a/resources/roles/install_uninstall/tasks/main.yml +++ b/resources/roles/install_uninstall/tasks/main.yml @@ -1,5 +1,11 @@ --- +- set_fact: + short_apk_pkg_name: "{{ pkg_name.split('=')[0] }}" + when: ansible_facts['os_family'] == 'Alpine' + tags: + - uninstall_package + - set_fact: short_deb_pkg_name: "{{ pkg_name.split('=')[0] }}" when: ansible_distribution_file_variety == 'Debian' @@ -83,3 +89,25 @@ when: ansible_distribution_file_variety == 'Debian' and deb_installed.rc == 0 tags: - uninstall_package + +- name: Install APK package + shell: + cmd: "apk --allow-untrusted add {{ pkg_name }}" + when: ansible_facts['os_family'] == 'Alpine' + tags: + - install_package + +- name: Check APK package is installed + shell: "apk --allow-untrusted info -e {{ short_apk_pkg_name }}" + register: apk_installed + failed_when: apk_installed.rc not in [0, 1] + when: ansible_facts['os_family'] == 'Alpine' + tags: + - uninstall_package + +- name: Uninstall APK package + shell: + cmd: "apk --allow-untrusted del {{ pkg_name }}" + when: ansible_facts['os_family'] == 'Alpine' and apk_installed.rc == 0 + tags: + - uninstall_package diff --git a/resources/roles/preparation/tasks/alpine.yml b/resources/roles/preparation/tasks/alpine.yml new file mode 100644 index 00000000..0475c421 --- /dev/null +++ b/resources/roles/preparation/tasks/alpine.yml @@ -0,0 +1,30 @@ +--- +- name: Update Alpine packages + command: apk --allow-untrusted update && apk --allow-untrusted upgrade + +- name: Add custom APK repository to /etc/apk/repositories + lineinfile: + path: /etc/apk/repositories + line: "{{ item.url }}" + state: present + with_items: "{{ repositories }}" + when: repositories is defined and repositories | length > 0 + +- name: Update APK index cache + command: apk --allow-untrusted update + +- name: Install required packages on Alpine + command: apk --allow-untrusted add ansible bats python3 py3-pip ca-certificates procps file iproute2 + +- name: Install test tools in virtualenv (Alpine) + pip: + name: + - pytest + - pytest-testinfra + - pytest-check + - pytest-tap + virtualenv: /opt/testenv + virtualenv_command: python3 -m venv + when: ansible_facts.os_family == 'Alpine' and pytest_is_needed | bool + tags: + - initial_provision diff --git a/resources/roles/preparation/tasks/main.yml b/resources/roles/preparation/tasks/main.yml index 9cb1d0a8..fbe87f28 100644 --- a/resources/roles/preparation/tasks/main.yml +++ b/resources/roles/preparation/tasks/main.yml @@ -14,6 +14,13 @@ tags: - initial_provision +- name: Initial provision for Alpine distributions + include_tasks: + file: alpine.yml + when: ansible_facts.os_family == 'Alpine' + tags: + - initial_provision + - name: Copy tests to test environment copy: src: "{{ integrity_tests_dir }}" @@ -29,7 +36,7 @@ - pytest-testinfra - pytest-check - pytest-tap - when: pytest_is_needed | bool + when: ansible_facts.os_family != 'Alpine' and pytest_is_needed | bool tags: - initial_provision @@ -44,6 +51,7 @@ with_items: - "/usr/lib64/firefox" - "/usr/lib64/thunderbird" + when: ansible_facts['distribution'] != 'Alpine' tags: - initial_provision @@ -51,3 +59,17 @@ shell: ldconfig tags: - initial_provision + when: ansible_facts['distribution'] != 'Alpine' + + +- name: Add custom LD_LIBRARY_PATH for Alpine + copy: + dest: /etc/profile.d/custom-libs.sh + content: | + export LD_LIBRARY_PATH=/usr/lib/firefox:/usr/lib/thunderbird:$LD_LIBRARY_PATH + owner: root + group: root + mode: 0644 + when: ansible_facts['distribution'] == 'Alpine' + tags: + - initial_provision