add --break for requesting a debug shell
Similar to rd.break for dracut this allows a user to specify:
- --break or --break=*
- to get a shell before each stage is run
- --break=stage.name
- to get a shell each time the stage with that name is run
- example: --break=org.osbuild.copy
- --break=stage.id
- to get a shell each time the stage with that ID is run
- get the ID for the stages for your manifest by running
osbuild on the manifest with --inspect
- example: --break=dc6e3a66fef3ebe7c815eb24d348215b9e5e2ed0cd808c15ebbe85fc73181a86
and get a bash shell where they can inspect the environment to debug
and develop OSBuild stages.
This commit is contained in:
parent
962b7f4d4b
commit
83a14886d3
4 changed files with 26 additions and 8 deletions
|
|
@ -57,6 +57,8 @@ is not listed here, **osbuild** will deny startup and exit with an error.
|
|||
--monitor-fd=NUM file-descriptor to be used for the monitor
|
||||
--stage-timeout set the maximal time (in seconds) each stage is
|
||||
allowed to run
|
||||
--break, --break=ID open debug shell when executing stages; accepts
|
||||
stage name or id (from --inspect) or * (for all)
|
||||
|
||||
NB: If neither ``--output-directory`` nor ``--checkpoint`` is specified, no
|
||||
attempt to build the manifest will be made.
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class ProcOverrides:
|
|||
self.overrides.add("cmdline")
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
# pylint: disable=too-many-instance-attributes,too-many-branches
|
||||
class BuildRoot(contextlib.AbstractContextManager):
|
||||
"""Build Root
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ class BuildRoot(contextlib.AbstractContextManager):
|
|||
if self._exitstack:
|
||||
self._exitstack.enter_context(api)
|
||||
|
||||
def run(self, argv, monitor, timeout=None, binds=None, readonly_binds=None, extra_env=None):
|
||||
def run(self, argv, monitor, timeout=None, binds=None, readonly_binds=None, extra_env=None, debug_shell=False):
|
||||
"""Runs a command in the buildroot.
|
||||
|
||||
Takes the command and arguments, as well as bind mounts to mirror
|
||||
|
|
@ -289,6 +289,7 @@ class BuildRoot(contextlib.AbstractContextManager):
|
|||
cmd += self.build_capabilities_args()
|
||||
|
||||
cmd += mounts
|
||||
debug_shell_cmd = cmd + ["--", "/bin/bash"] # used for debugging if requested
|
||||
cmd += ["--", runner]
|
||||
cmd += argv
|
||||
|
||||
|
|
@ -304,6 +305,11 @@ class BuildRoot(contextlib.AbstractContextManager):
|
|||
if extra_env:
|
||||
env.update(extra_env)
|
||||
|
||||
# If the user requested it then break into a shell here
|
||||
# for debugging.
|
||||
if debug_shell:
|
||||
subprocess.run(debug_shell_cmd, check=True)
|
||||
|
||||
proc = subprocess.Popen(cmd,
|
||||
bufsize=0,
|
||||
env=env,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ def parse_arguments(sys_argv):
|
|||
parser.add_argument("--version", action="version",
|
||||
help="return the version of osbuild",
|
||||
version="%(prog)s " + osbuild.__version__)
|
||||
# nargs='?' const='*' means `--break` is equivalent to `--break=*`
|
||||
parser.add_argument("--break", dest='debug_break', type=str, nargs='?', const='*',
|
||||
help="open debug shell when executing stage. Accepts stage name or id or * (for all)")
|
||||
|
||||
return parser.parse_args(sys_argv[1:])
|
||||
|
||||
|
|
@ -163,6 +166,7 @@ def osbuild_cli():
|
|||
object_store.maximum_size = args.cache_max_size
|
||||
|
||||
stage_timeout = args.stage_timeout
|
||||
debug_break = args.debug_break
|
||||
|
||||
pipelines = manifest.depsolve(object_store, exports)
|
||||
|
||||
|
|
@ -173,6 +177,7 @@ def osbuild_cli():
|
|||
pipelines,
|
||||
monitor,
|
||||
args.libdir,
|
||||
debug_break,
|
||||
stage_timeout=stage_timeout
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ class Stage:
|
|||
with open(location, "w", encoding="utf-8") as fp:
|
||||
json.dump(args, fp)
|
||||
|
||||
def run(self, tree, runner, build_tree, store, monitor, libdir, timeout=None):
|
||||
def run(self, tree, runner, build_tree, store, monitor, libdir, debug_break="", timeout=None):
|
||||
with contextlib.ExitStack() as cm:
|
||||
|
||||
build_root = buildroot.BuildRoot(build_tree, runner.path, libdir, store.tmp)
|
||||
|
|
@ -234,12 +234,15 @@ class Stage:
|
|||
if self.source_epoch is not None:
|
||||
extra_env["SOURCE_DATE_EPOCH"] = str(self.source_epoch)
|
||||
|
||||
debug_shell = debug_break in ('*', self.name, self.id)
|
||||
|
||||
r = build_root.run([f"/run/osbuild/bin/{self.name}"],
|
||||
monitor,
|
||||
timeout=timeout,
|
||||
binds=binds,
|
||||
readonly_binds=ro_binds,
|
||||
extra_env=extra_env)
|
||||
extra_env=extra_env,
|
||||
debug_shell=debug_shell)
|
||||
|
||||
return BuildResult(self, r.returncode, r.output, api.error)
|
||||
|
||||
|
|
@ -290,7 +293,7 @@ class Pipeline:
|
|||
self.assembler.base = stage.id
|
||||
return stage
|
||||
|
||||
def build_stages(self, object_store, monitor, libdir, stage_timeout=None):
|
||||
def build_stages(self, object_store, monitor, libdir, debug_break="", stage_timeout=None):
|
||||
results = {"success": True}
|
||||
|
||||
# If there are no stages, just return here
|
||||
|
|
@ -348,6 +351,7 @@ class Pipeline:
|
|||
object_store,
|
||||
monitor,
|
||||
libdir,
|
||||
debug_break,
|
||||
stage_timeout)
|
||||
|
||||
monitor.result(r)
|
||||
|
|
@ -365,13 +369,14 @@ class Pipeline:
|
|||
|
||||
return results
|
||||
|
||||
def run(self, store, monitor, libdir, stage_timeout=None):
|
||||
def run(self, store, monitor, libdir, debug_break="", stage_timeout=None):
|
||||
|
||||
monitor.begin(self)
|
||||
|
||||
results = self.build_stages(store,
|
||||
monitor,
|
||||
libdir,
|
||||
debug_break,
|
||||
stage_timeout)
|
||||
|
||||
monitor.finish(results)
|
||||
|
|
@ -461,11 +466,11 @@ class Manifest:
|
|||
|
||||
return list(map(lambda x: x.name, reversed(build.values())))
|
||||
|
||||
def build(self, store, pipelines, monitor, libdir, stage_timeout=None):
|
||||
def build(self, store, pipelines, monitor, libdir, debug_break="", stage_timeout=None):
|
||||
results = {"success": True}
|
||||
|
||||
for pl in map(self.get, pipelines):
|
||||
res = pl.run(store, monitor, libdir, stage_timeout)
|
||||
res = pl.run(store, monitor, libdir, debug_break, stage_timeout)
|
||||
results[pl.id] = res
|
||||
if not res["success"]:
|
||||
results["success"] = False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue