Add support for "compose exec"

Add support to run shell commands inside a checked out
ostree branch.

The way that this works is the following:

1. Determine the branch to checkout.
2. Checkout the branch in the workspace.
3. Use systemd-nspawn to create a shell inside
   the branch.

Test Plan
PASSED Installed apt-ostree from git repo.
PASSED Run sudo apt-ostree compose create \
           --base config/debian/bookworm \
           --repo /repo debian/bookworm \
           bookworm-test
PASSED Run sudo apt-ostree compose exec --repo /repo --branch
       bookwork-test "cat /etc/debian_version"

Story: 2010867
Task: 48556

Change-Id: I6ff9658fc04ec83d50d2cb5f3b8dbb70e3065688
Signed-off-by: Charles Short <charles.short@windriver.com>
This commit is contained in:
Charles Short 2023-10-24 12:28:42 -04:00
parent a6b5b626a4
commit 4f1bba5c62
4 changed files with 112 additions and 0 deletions

View File

@ -15,6 +15,7 @@ from apt_ostree.cmd.compose.init import init
from apt_ostree.cmd.compose.install import install
from apt_ostree.cmd.compose.repo import repo
from apt_ostree.cmd.compose.restore import restore
from apt_ostree.cmd.compose.run import run
from apt_ostree.cmd.compose.uninstall import uninstall
from apt_ostree.cmd.compose.upgrade import upgrade
@ -34,4 +35,5 @@ compose.add_command(install)
compose.add_command(upgrade)
compose.add_command(repo)
compose.add_command(restore)
compose.add_command(run)
compose.add_command(uninstall)

View File

@ -0,0 +1,54 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import errno
import sys
import click
from apt_ostree.cmd import pass_state_context
from apt_ostree.run import RunCommand
@click.command(
name="exec",
help="Run a command or shell from an Ostree branch.")
@pass_state_context
@click.option(
"--mounts",
help="Path to yaml configuration for mount points.",
type=click.Path(exists=True)
)
@click.option(
"--root",
help="Path to operate on",
)
@click.option(
"--pre-exec",
'pre_exec',
help="Run the command before executing the container",
)
@click.argument(
"cmd",
# If command not specified then execute a shell.
default=""
)
def run(state,
mounts,
root,
pre_exec,
cmd):
try:
RunCommand(state).run_command(cmd, mounts, pre_exec, root)
except KeyboardInterrupt:
click.secho("\n" + ("Exiting at your request."))
sys.exit(130)
except BrokenPipeError:
sys.exit()
except OSError as error:
if error.errno == errno.ENOSPC:
sys.exit("error - No space left on device.")

53
apt_ostree/run.py Normal file
View File

@ -0,0 +1,53 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
import sys
import click
from rich.console import Console
import yaml
from apt_ostree import utils
class RunCommand:
def __init__(self, state):
self.state = state
self.console = Console()
def run_command(self, command, mount_points, pre_exec, rootfs):
"""Run a command in an ostree branch."""
if not os.path.exists(rootfs):
click.secho(f"Directory not found: {rootfs}", fg="red")
sys.exit(1)
mounts = None
if mount_points:
self.console.print("Loading configuration.")
with open(mount_points, "r") as f:
mounts = yaml.safe_load(f)
if pre_exec:
self.console.print(f"Executing pre-command: {pre_exec}",
highlight=False)
cmd = ["systemd-nspawn", "-q", "-D", rootfs]
cmd += pre_exec.split()
r = utils.run_command(cmd)
if r.returncode != 0:
self.console.print("Sucessfully executed pre-command")
# Run the systemd-nspawn command.
cmd = ["systemd-nspawn", "-q"]
if mounts:
for m in mounts:
cmd += (["--bind", f"{m}"])
cmd += ["-D", rootfs]
if len(command) != 0:
cmd += command.split()
utils.run_command(cmd, check=False)

3
etc/mounts.yaml Normal file
View File

@ -0,0 +1,3 @@
# example configuration for apt-ostree compose exec
---
- /var/www/html/repo:/ostree