Lindley Werner 407a3151a5 Build OSTree commit using component
When installing Debian packages, apt-ostree can reference packages
from an specific component. Each component is related to a patch
version (sw_release).

Test Plan:
PASS Install apt-ostree from git repo.
PASS Run "apt-ostree compose create \
--base config/debian/bookworm \
--repo /repo debian/bookworm \
bookworm-test"
PASS Run "apt-ostree compose install --repo /repo \
--branch bookworm-test --component <sw_release> docker.io"
PASS Check the output of "ostree log --repo /repo bookworm-test"

Story: 2010867
Task: 50184

Change-Id: I33cc01d1b2f8a9212b7b96591165334c25f9ceab
Signed-off-by: Lindley Werner <Lindley.Vieira@windriver.com>
2024-05-29 16:00:37 +00:00

165 lines
6.0 KiB
Python

"""
Copyright (c) 2023-2024 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import logging
import os
import shutil
import subprocess
import sys
import textwrap
from rich.console import Console
from apt_ostree.ostree import Ostree
from apt_ostree import utils
APT_CONF = "etc/apt/sources.list.d/apt-ostree.list"
class Deploy:
def __init__(self, state):
self.console = Console()
self.logging = logging.getLogger(__name__)
self.state = state
self.ostree = Ostree(self.state)
self.workspace = self.state.workspace
self.workdir = self.state.workspace.joinpath("deployment")
self.workdir.mkdir(parents=True, exist_ok=True)
self.rootfs = None
def prestaging(self, rootfs, feed=None, component=None):
"""Pre stage steps."""
if not rootfs.exists():
self.logging.error("rootfs not found: {rootfs}")
sys.exit(1)
self.logging.info("Running prestaging steps.")
with self.console.status("Running prestaging steps."):
fd = os.open(rootfs, os.O_DIRECTORY)
# Ensure that we correct permissions
os.chown(rootfs, 0, 0)
if not rootfs.joinpath("etc").exists():
self.logging.info("Converting /usr/etc -> /etc.")
os.symlink("usr/etc", "etc", dir_fd=fd)
os.chown(rootfs.joinpath("usr/etc"), 0, 0)
if rootfs.joinpath("usr/rootdirs/var/lib/dpkg").exists():
# /var is suppose to hold the state of the running
# system.
self.logging.info("Configuring package manager.")
os.rmdir("var", dir_fd=fd)
os.symlink("usr/rootdirs/var", "var", dir_fd=fd)
r = utils.run_command(
["systemd-tmpfiles", f"--root={rootfs}", "--create",
"--boot", "--prefix=/var"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# According to systemd-tmpfiles(8), the return values are:
# 0 - success
# 65 - so some lines had to be ignored, but no other errors
# 73 - configuration ok, but could not be created
# 1 - other error
if r.returncode not in [0, 65]:
self.logging.error("Failed to populate /var,")
self.logging.error(f"Error code: {r.returncode}")
sys.exit(1)
if feed:
self.logging.info("Configuring temporary package feed.")
self.apt_conf = rootfs.joinpath(APT_CONF)
feed = feed.replace('"', '')
conf_repo_line = f"deb [trusted=yes] {feed}"
if component:
self.logging.info(
f"Configuring temp package component: {component}")
component = component.replace('"', '')
conf_repo_line = f"deb [trusted=yes] {feed} . {component}"
self.apt_conf.write_text(textwrap.dedent(conf_repo_line))
def poststaging(self, rootfs):
"""Post staging steps."""
if not rootfs.exists():
self.logging.error("rootfs not found: {rootfs}")
sys.exit(1)
self.logging.info("Running post deploy steps.")
fd = os.open(rootfs, os.O_DIRECTORY)
if os.path.exists(rootfs.joinpath("etc")):
self.logging.info("Converting /etc -> /usr/etc.")
os.unlink("etc", dir_fd=fd)
os.chown(rootfs.joinpath("usr/etc"), 0, 0)
if rootfs.joinpath("usr/rootdirs/var/lib/dpkg").exists():
# /var is stateless so remove everything that
# might be lying around.
self.logging.info("Configuring package manager.")
os.chown(rootfs.joinpath("usr/rootdirs/var"), 0, 0)
if rootfs.joinpath("var"):
os.unlink("var", dir_fd=fd)
os.mkdir("var", dir_fd=fd, mode=0o755)
apt_conf = rootfs.joinpath(APT_CONF)
if apt_conf.exists():
apt_conf.unlink()
def cleanup(self, rootfs):
"""Remove workspace directores to save space."""
self.logging.info("Cleaning up.")
if rootfs.exists():
shutil.rmtree(rootfs)
def get_sysroot(self, branch=None):
"""Checkout the commit to the specified directory."""
if branch is None:
branch = self.ostree.get_branch()
rev = self.ostree.ostree_ref(branch)
self.logging.info(f"Checking out {rev[:10]} from {branch}.")
with self.console.status(f"Checking out {rev[:10]}..."):
self.workdir = self.workdir.joinpath(branch)
self.workdir.mkdir(parents=True, exist_ok=True)
self.rootfs = self.workdir.joinpath(rev)
if self.rootfs.exists():
shutil.rmtree(self.rootfs)
self.ostree.ostree_checkout(branch, self.rootfs)
return self.rootfs
def deploy(self, reboot):
"""Run ostree admin deploy."""
ref = self.ostree.ostree_ref(self.state.branch)
if not ref:
self.logging.error(
f"Unable to find branch: {self.state.branch}.")
sys.exit(1)
r = utils.run_command(
["ostree", "admin", "deploy", self.state.branch]
)
if r.returncode != 0:
self.logging.error("Failed to deploy.")
sys.exit(1)
if reboot:
self.logging.info("Updating grub.")
r = utils.run_command(
["update-grub"]
)
if r.returncode != 0:
self.logging.error("Failed to update grub.")
sys.exit(1)
self.logging.info("Rebooting now.")
r = utils.run_command(
["shutdown", "-r", "now"]
)
if r.returncode != 0:
self.logging.error("Failed to reboot.")
sys.exit(1)
else:
self.logging.info("Please reboot for the changes to take affect.")