From b76916e9ef986e9840008f0bc62cc999ac8b05e9 Mon Sep 17 00:00:00 2001 From: Oliver Kurth Date: Wed, 31 Jul 2024 20:16:06 +0000 Subject: [PATCH 1/4] changes for installing Fedora --- photon_installer/installer.py | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/photon_installer/installer.py b/photon_installer/installer.py index 4b27860..b93f08b 100644 --- a/photon_installer/installer.py +++ b/photon_installer/installer.py @@ -49,6 +49,9 @@ class PartitionType(Enum): BIOS = 5 +RHEL_LIKES=["fedora", "rocky", "rhel", "centos"] + + class Installer(object): """ Photon installer @@ -66,6 +69,7 @@ class Installer(object): 'bootmode', 'disk', 'disks', + 'dist', 'docker', 'dps', 'eject_cdrom', @@ -93,6 +97,7 @@ class Installer(object): 'prepkgsinstallscripts', 'public_key', 'photon_docker_image', + 'release_package', 'repos', 'search_path', 'setup_grub_script', @@ -327,6 +332,10 @@ def _add_defaults(self, install_config): else: arch = install_config['arch'] + if 'dist' not in install_config: + install_config['dist'] = "photon" + dist = install_config['dist'] + # 'bootmode' mode if 'bootmode' not in install_config: if "x86_64" in arch: @@ -371,7 +380,9 @@ def _add_defaults(self, install_config): # add bootloader packages after bootmode set if install_config['bootmode'] in ['dualboot', 'efi']: - packages.append('grub2-efi-image') + dist = install_config.get('dist', "photon") + if dist == "photon": + packages.append("grub2-efi-image") # ansible needs python3 in the target if 'ansible' in install_config: @@ -450,7 +461,7 @@ def _add_defaults(self, install_config): # define 'hostname' as 'photon-' if "hostname" not in install_config or install_config['hostname'] == "": - install_config['hostname'] = f'photon-{secrets.randbelow(16 ** 12):12x}' + install_config['hostname'] = f"{dist}-{secrets.randbelow(16 ** 12):12x}" # Set password if needed. # Installer uses 'shadow_password' and optionally 'password'/'age' @@ -1225,8 +1236,15 @@ def _initialize_system(self): self.logger.error("Failed to initialize rpm DB") self.exit_gracefully() - retval = self.tdnf.run(['install', 'filesystem'], + packages = ["filesystem"] + # in RHEL like OSes, "filesystem" will pull in a release package, like "fedora-release-basic". + # This can later conflict with any other release package if installed later + if 'release_package' in self.install_config: + packages.append(self.install_config['release_package']) + + retval = self.tdnf.run(["install"] + packages, repos=self.install_config['repos'], do_json=False) + if retval != 0: self.logger.error("Failed to install filesystem rpm") self.exit_gracefully() @@ -1503,13 +1521,26 @@ def _install_packages(self): """ Install packages using tdnf command """ - self._adjust_packages_based_on_selected_flavor() + + if self.install_config['dist'] == "photon": + self._adjust_packages_based_on_selected_flavor() + selected_packages = self.install_config['packages'] state = 0 packages_to_install = {} total_size = 0 stderr = None + dracut_conffile = None + if self.install_config['dist'] in RHEL_LIKES: + # make sure to build initramfs with hostonly="no" for a generic kernel + dracut_confdir = os.path.join(self.photon_root, "etc", "dracut.conf.d") + os.makedirs(dracut_confdir) + # Fedora sets hostonly="yes" in 01-dist.conf, override it + dracut_conffile = os.path.join(dracut_confdir, "01-dist-poi.conf") + with open(dracut_conffile, "wt") as f: + f.write(f'hostonly="no"\n') + if self.install_config['ui']: tdnf_cmd = ("tdnf install -y --releasever {0} --installroot {1} " "-c {2} --setopt=reposdir={3} " @@ -1569,6 +1600,11 @@ def _install_packages(self): self.logger.error(stderr.decode()) self.exit_gracefully() + # remove again, assuming kernel upgrades will be done in a real host using hostonly="yes" + if dracut_conffile is not None: + os.remove(dracut_conffile) + + def _eject_cdrom(self): """ Eject the cdrom on request @@ -1580,8 +1616,11 @@ def _enable_network_in_chroot(self): """ Enable network in chroot """ - if os.path.exists("/etc/resolv.conf"): - shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') + try: + if os.path.exists("/etc/resolv.conf"): + shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') + except FileNotFoundError: + pass def _deactivate_network_in_chroot(self): """ From 16f8d5cd92874f18ec1507c404e4ffb49a8d6f4a Mon Sep 17 00:00:00 2001 From: Oliver Kurth Date: Thu, 1 Aug 2024 21:14:07 +0000 Subject: [PATCH 2/4] refactor m_updaterootpassword.py --- .../modules/m_updaterootpassword.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/photon_installer/modules/m_updaterootpassword.py b/photon_installer/modules/m_updaterootpassword.py index fe1205b..b2611ee 100644 --- a/photon_installer/modules/m_updaterootpassword.py +++ b/photon_installer/modules/m_updaterootpassword.py @@ -3,13 +3,29 @@ # * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-only # */ -import os import commons +import os +import shutil + install_phase = commons.POST_INSTALL enabled = True +def replace_passwd(filepath, passwd, user="root"): + + filepath_tmp = f"{filepath}.tmp" + with open(filepath, "rt") as fin: + with open(filepath_tmp, "wt") as fout: + for line in fin: + l = line.split(":") + if l[0] == user: + l[1] = passwd + fout.write(":".join(l)) + shutil.copystat(filepath, filepath_tmp) + os.rename(filepath_tmp, filepath) + + def execute(installer): shadow_password = installer.install_config['shadow_password'] installer.logger.info("Set root password") @@ -18,19 +34,13 @@ def execute(installer): shadow_filename = os.path.join(installer.photon_root, 'etc/shadow') # replace root blank password in passwd file to point to shadow file - commons.replace_string_in_file(passwd_filename, "root::", "root:x:") + replace_passwd(passwd_filename, "x") if not os.path.isfile(shadow_filename): with open(shadow_filename, "w") as destination: - destination.write("root:" + shadow_password + ":") + destination.write(f"root:{shadow_password}:") else: - # add password hash in shadow file - commons.replace_string_in_file( - shadow_filename, "root::", f"root:{shadow_password}:" - ) - commons.replace_string_in_file( - shadow_filename, "root:x:", f"root:{shadow_password}:" - ) + replace_passwd(shadow_filename, shadow_password) installer.cmd.run_in_chroot(installer.photon_root, "/usr/sbin/pwconv") installer.cmd.run_in_chroot(installer.photon_root, "/usr/sbin/grpconv") From 442fcf63098a21618d58f6d28c834b85ab79ded3 Mon Sep 17 00:00:00 2001 From: Oliver Kurth Date: Fri, 2 Aug 2024 00:10:17 +0000 Subject: [PATCH 3/4] refactor m_updatehostname.py, use 127.0.1.1 for own hostname --- photon_installer/modules/m_updatehostname.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/photon_installer/modules/m_updatehostname.py b/photon_installer/modules/m_updatehostname.py index cd865b1..9d89b40 100644 --- a/photon_installer/modules/m_updatehostname.py +++ b/photon_installer/modules/m_updatehostname.py @@ -17,9 +17,8 @@ def execute(installer): hostname_file = os.path.join(installer.photon_root, "etc/hostname") hosts_file = os.path.join(installer.photon_root, "etc/hosts") - with open(hostname_file, "wb") as outfile: - outfile.write(hostname.encode()) + with open(hostname_file, "wt") as fout: + fout.write(hostname) - pattern = r'(127\.0\.0\.1)(\s+)(localhost)\s*\Z' - replace = r'\1\2\3\n\1\2' + hostname - commons.replace_string_in_file(hosts_file, pattern, replace) + with open(hosts_file, "at") as fout: + fout.write(f"127.0.1.1 {hostname}\n") From 3e76f17427d082b34a5013e36613ee3b96911f56 Mon Sep 17 00:00:00 2001 From: Oliver Kurth Date: Thu, 12 Sep 2024 21:17:12 +0000 Subject: [PATCH 4/4] fix .github/workflows, no need for a docker proxy --- .github/workflows/photon-os-installer.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/photon-os-installer.yml b/.github/workflows/photon-os-installer.yml index 7991891..7f618d3 100644 --- a/.github/workflows/photon-os-installer.yml +++ b/.github/workflows/photon-os-installer.yml @@ -16,7 +16,7 @@ jobs: run: | POI_IMAGE_NAME=photon/installer:${{ github.sha }} cd docker - docker build -t $POI_IMAGE_NAME --build-arg DOCKER_PROXY=dockerhub.artifactory.eng.vmware.com/ --build-context poi-helper=$(realpath $(pwd)/..) . + docker build -t $POI_IMAGE_NAME --build-context poi-helper=$(realpath $(pwd)/..) . docker system prune -f test: @@ -37,6 +37,9 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt # Ensure this file lists the necessary dependencies + - name: install pytest + run: pip install pytest + - name: Run Pytest run: | pytest -x tests/poi-container-test.py