daisy_workflows/image_build/install_package/installpackage.py (70 lines of code) (raw):
#!/usr/bin/env python3
import logging
import os
import subprocess
import utils
from utils.common import _GetMetadataParam
def run(cmd, capture_output=True, check=True, encoding='utf-8'):
logging.info('Run: %s', cmd)
return subprocess.run(cmd.split(), capture_output=capture_output,
check=check, encoding=encoding)
def get_mount_disk(image):
devname = _GetMetadataParam('disks/1/device-name', raise_on_not_found=True)
devicepath = f'/dev/disk/by-id/google-{devname}'
gpt = get_part_type(devicepath) == 'gpt'
# This assumes that, for UEFI systems:
# 1. partition 1 is the EFI system partition.
# 2. partition 2 is the root mount for the installed system.
#
# Except on debian, which has out-of-order partitions.
if gpt and 'debian' not in image:
return f'{devicepath}-part2'
else:
return f'{devicepath}-part1'
def get_part_type(device):
ret = run(f'blkid -s PTTYPE -o value {device}')
return ret.stdout.strip()
def get_distro_from_image(image):
el_distros = ('centos', 'rhel', 'almalinux', 'rocky-linux')
if any([x in image for x in el_distros]):
return 'enterprise_linux'
elif 'debian' in image:
return 'debian'
else:
return None
def main():
image = utils.GetMetadataAttribute('image', raise_on_not_found=True)
package = utils.GetMetadataAttribute('gcs_package_path',
raise_on_not_found=True)
package_name = package.split('/')[-1]
mount_disk = get_mount_disk(image)
logging.info('Mount device %s at /mnt', mount_disk)
run(f'mount {mount_disk} /mnt')
# The rpm utility requires /dev/random to initialize GnuTLS
logging.info('Mount dev filesystem in chroot')
run('mount -o bind /dev /mnt/dev')
# Enable DNS resolution in the chroot, for fetching dependencies.
if os.path.isfile('/mnt/etc/resolv.conf'):
os.rename('/mnt/etc/resolv.conf', '/mnt/etc/resolv.conf.bak')
utils.WriteFile('/mnt/etc/resolv.conf', utils.ReadFile('/etc/resolv.conf'))
utils.DownloadFile(package, f'/mnt/tmp/{package_name}')
distribution = get_distro_from_image(image)
if distribution == 'debian':
install_cmd = 'apt install -y '
elif distribution == 'enterprise_linux':
install_cmd = 'dnf install -y'
else:
raise Exception('Unknown Linux distribution.')
logging.info('Installing package %s', package_name)
run(f'chroot /mnt {install_cmd} /tmp/{package_name}')
if distribution == 'enterprise_linux':
run('chroot /mnt /sbin/setfiles -v -F '
'/etc/selinux/targeted/contexts/files/file_contexts /')
os.remove('/mnt/etc/resolv.conf')
# Restore resolv.conf if necessary
if os.path.isfile('/mnt/etc/resolv.conf.bak'):
os.rename('/mnt/etc/resolv.conf.bak', '/mnt/etc/resolv.conf')
# Best effort to unmount prior to shutdown.
run('sync', check=False)
run('umount /mnt/dev', check=False)
run('umount /mnt', check=False)
logging.success('Package %s installed successfully', package_name)
if __name__ == '__main__':
try:
main()
except subprocess.CalledProcessError as e:
logging.info('stdout: %s', e.stdout)
logging.info('stderr: %s', e.stderr)
logging.error('failed to execute cmd: %s', e)
except Exception as e:
logging.error('%s', e)