ybox 0.9.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ybox/__init__.py +2 -0
- ybox/cmd.py +307 -0
- ybox/conf/completions/ybox.fish +93 -0
- ybox/conf/distros/arch/add-gpg-key.sh +29 -0
- ybox/conf/distros/arch/distro.ini +192 -0
- ybox/conf/distros/arch/init-base.sh +10 -0
- ybox/conf/distros/arch/init-user.sh +35 -0
- ybox/conf/distros/arch/init.sh +82 -0
- ybox/conf/distros/arch/list_fmt_long.py +76 -0
- ybox/conf/distros/arch/pkgdeps.py +276 -0
- ybox/conf/distros/deb-generic/check-package.sh +77 -0
- ybox/conf/distros/deb-generic/distro.ini +190 -0
- ybox/conf/distros/deb-generic/fetch-gpg-key-id.sh +30 -0
- ybox/conf/distros/deb-generic/init-base.sh +11 -0
- ybox/conf/distros/deb-generic/init-user.sh +3 -0
- ybox/conf/distros/deb-generic/init.sh +136 -0
- ybox/conf/distros/deb-generic/list_fmt_long.py +114 -0
- ybox/conf/distros/deb-generic/pkgdeps.py +208 -0
- ybox/conf/distros/deb-oldstable/distro.ini +21 -0
- ybox/conf/distros/deb-stable/distro.ini +21 -0
- ybox/conf/distros/supported.list +5 -0
- ybox/conf/distros/ubuntu2204/distro.ini +21 -0
- ybox/conf/distros/ubuntu2404/distro.ini +21 -0
- ybox/conf/profiles/apps.ini +26 -0
- ybox/conf/profiles/basic.ini +310 -0
- ybox/conf/profiles/dev.ini +25 -0
- ybox/conf/profiles/games.ini +39 -0
- ybox/conf/resources/entrypoint-base.sh +170 -0
- ybox/conf/resources/entrypoint-common.sh +23 -0
- ybox/conf/resources/entrypoint-cp.sh +32 -0
- ybox/conf/resources/entrypoint-root.sh +20 -0
- ybox/conf/resources/entrypoint-user.sh +21 -0
- ybox/conf/resources/entrypoint.sh +249 -0
- ybox/conf/resources/prime-run +13 -0
- ybox/conf/resources/run-in-dir +60 -0
- ybox/conf/resources/run-user-bash-cmd +14 -0
- ybox/config.py +255 -0
- ybox/env.py +205 -0
- ybox/filelock.py +77 -0
- ybox/migrate/0.9.0-0.9.7:0.9.8.py +33 -0
- ybox/pkg/__init__.py +0 -0
- ybox/pkg/clean.py +33 -0
- ybox/pkg/info.py +40 -0
- ybox/pkg/inst.py +638 -0
- ybox/pkg/list.py +191 -0
- ybox/pkg/mark.py +68 -0
- ybox/pkg/repair.py +150 -0
- ybox/pkg/repo.py +251 -0
- ybox/pkg/search.py +52 -0
- ybox/pkg/uninst.py +92 -0
- ybox/pkg/update.py +56 -0
- ybox/print.py +121 -0
- ybox/run/__init__.py +0 -0
- ybox/run/cmd.py +54 -0
- ybox/run/control.py +102 -0
- ybox/run/create.py +1116 -0
- ybox/run/destroy.py +64 -0
- ybox/run/graphics.py +367 -0
- ybox/run/logs.py +57 -0
- ybox/run/ls.py +64 -0
- ybox/run/pkg.py +445 -0
- ybox/schema/0.9.1-added.sql +27 -0
- ybox/schema/0.9.6-added.sql +18 -0
- ybox/schema/init.sql +39 -0
- ybox/schema/migrate/0.9.0:0.9.1.sql +42 -0
- ybox/schema/migrate/0.9.1:0.9.2.sql +8 -0
- ybox/schema/migrate/0.9.2:0.9.3.sql +2 -0
- ybox/schema/migrate/0.9.5:0.9.6.sql +2 -0
- ybox/state.py +914 -0
- ybox/util.py +351 -0
- ybox-0.9.8.dist-info/LICENSE +19 -0
- ybox-0.9.8.dist-info/METADATA +533 -0
- ybox-0.9.8.dist-info/RECORD +76 -0
- ybox-0.9.8.dist-info/WHEEL +5 -0
- ybox-0.9.8.dist-info/entry_points.txt +8 -0
- ybox-0.9.8.dist-info/top_level.txt +1 -0
ybox/pkg/search.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Methods for searching packages in the repositories matching given search terms.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import sys
|
7
|
+
from configparser import SectionProxy
|
8
|
+
|
9
|
+
from ybox.cmd import PkgMgr, page_command
|
10
|
+
from ybox.config import StaticConfiguration
|
11
|
+
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
|
+
|
13
|
+
|
14
|
+
# noinspection PyUnusedLocal
|
15
|
+
def search_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
16
|
+
conf: StaticConfiguration, runtime_conf: RuntimeConfiguration,
|
17
|
+
state: YboxStateManagement) -> int:
|
18
|
+
# pylint: disable=unused-argument
|
19
|
+
"""
|
20
|
+
Uninstall package specified by `args.package` on a ybox container with given podman/docker
|
21
|
+
command. Additional flags honored are `args.quiet` to bypass user confirmation during
|
22
|
+
uninstall, `args.keep_config_files` to keep the system configuration and/or data files
|
23
|
+
of the package, `args.skip_deps` to skip removal of all orphaned dependencies of the package
|
24
|
+
(including required and optional dependencies).
|
25
|
+
|
26
|
+
:param args: arguments having `search` and all other attributes passed by the user
|
27
|
+
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
28
|
+
:param docker_cmd: the podman/docker executable to use
|
29
|
+
:param conf: the :class:`StaticConfiguration` for the container
|
30
|
+
:param runtime_conf: the `RuntimeConfiguration` of the container
|
31
|
+
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
32
|
+
:return: integer exit status of search command where 0 represents success
|
33
|
+
"""
|
34
|
+
quiet_flag = pkgmgr[PkgMgr.QUIET_DETAILS_FLAG.value] if args.quiet else ""
|
35
|
+
official = pkgmgr[PkgMgr.SEARCH_OFFICIAL_FLAG.value] if args.official else ""
|
36
|
+
word_start = word_end = ""
|
37
|
+
if args.word:
|
38
|
+
word_start = pkgmgr[PkgMgr.SEARCH_WORD_START_FLAG.value]
|
39
|
+
word_end = pkgmgr[PkgMgr.SEARCH_WORD_END_FLAG.value]
|
40
|
+
search_terms: list[str] = args.search # there will be at least one search term in the list
|
41
|
+
search = pkgmgr[PkgMgr.SEARCH_ALL.value] if args.all else pkgmgr[PkgMgr.SEARCH.value]
|
42
|
+
# quote the search terms for bash to properly see the full arguments if they contain spaces
|
43
|
+
# or other special characters
|
44
|
+
search_cmd = search.format(quiet=quiet_flag, official=official, word_start=word_start,
|
45
|
+
word_end=word_end, search="'" + "' '".join(search_terms) + "'")
|
46
|
+
docker_args = [docker_cmd, "exec"]
|
47
|
+
if sys.stdout.isatty(): # don't act as a terminal if it is being redirected
|
48
|
+
docker_args.append("-it")
|
49
|
+
docker_args.extend([conf.box_name, "/bin/bash", "-c", search_cmd])
|
50
|
+
# empty pager argument is a valid one and indicates no pagination, hence the `is None` check
|
51
|
+
pager: str = args.pager if args.pager is not None else conf.pager
|
52
|
+
return page_command(docker_args, pager, error_msg="searching repositories")
|
ybox/pkg/uninst.py
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
"""
|
2
|
+
Methods for uninstalling package uninstallation on an active ybox container.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
from configparser import SectionProxy
|
7
|
+
|
8
|
+
from ybox.cmd import PkgMgr, build_shell_command, run_command
|
9
|
+
from ybox.config import StaticConfiguration
|
10
|
+
from ybox.print import print_error, print_info, print_notice
|
11
|
+
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
|
+
from ybox.util import check_package, select_item_from_menu
|
13
|
+
|
14
|
+
|
15
|
+
def uninstall_package(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
16
|
+
conf: StaticConfiguration, runtime_conf: RuntimeConfiguration,
|
17
|
+
state: YboxStateManagement) -> int:
|
18
|
+
"""
|
19
|
+
Uninstall package specified by `args.package` on a ybox container with given podman/docker
|
20
|
+
command. Additional flags honored are `args.quiet` to bypass user confirmation during
|
21
|
+
uninstall, `args.keep_config_files` to keep the system configuration and/or data files
|
22
|
+
of the package, `args.skip_deps` to skip removal of all orphaned dependencies of the package
|
23
|
+
(including required and optional dependencies).
|
24
|
+
|
25
|
+
:param args: arguments having `package` and all other attributes passed by the user
|
26
|
+
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
27
|
+
:param docker_cmd: the podman/docker executable to use
|
28
|
+
:param conf: the :class:`StaticConfiguration` for the container
|
29
|
+
:param runtime_conf: the `RuntimeConfiguration` of the container
|
30
|
+
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
31
|
+
:return: integer exit status of uninstall command where 0 represents success
|
32
|
+
"""
|
33
|
+
package: str = args.package
|
34
|
+
quiet_flag = pkgmgr[PkgMgr.QUIET_FLAG.value] if args.quiet else ""
|
35
|
+
purge_flag = "" if args.keep_config_files else pkgmgr[PkgMgr.PURGE_FLAG.value]
|
36
|
+
remove_deps_flag = "" if args.skip_deps else pkgmgr[PkgMgr.REMOVE_DEPS_FLAG.value]
|
37
|
+
uninstall_cmd = pkgmgr[PkgMgr.UNINSTALL.value].format(
|
38
|
+
quiet=quiet_flag, purge=purge_flag, remove_deps=remove_deps_flag, package="{package}")
|
39
|
+
check_cmd = pkgmgr[PkgMgr.CHECK_INSTALL.value]
|
40
|
+
return _uninstall_package(package, args.skip_deps, uninstall_cmd, check_cmd, docker_cmd, conf,
|
41
|
+
runtime_conf, state)
|
42
|
+
|
43
|
+
|
44
|
+
def _uninstall_package(package: str, skip_deps: bool, uninstall_cmd: str, check_cmd: str,
|
45
|
+
docker_cmd: str, conf: StaticConfiguration,
|
46
|
+
runtime_conf: RuntimeConfiguration, state: YboxStateManagement,
|
47
|
+
dep_msg: str = "") -> int:
|
48
|
+
"""
|
49
|
+
Real workhorse of :func:`uninstall_package` that uninstalls given package on a ybox container
|
50
|
+
with given podman/docker command.
|
51
|
+
|
52
|
+
:param package: the package to be uninstalled
|
53
|
+
:param skip_deps: true if orphaned dependencies of the package should not be uninstalled
|
54
|
+
:param uninstall_cmd: fully formed uninstallation command from the distribution's `distro.ini`
|
55
|
+
:param check_cmd: command to check for existence of the package returning the resolved name
|
56
|
+
as read from distribution's `distro.ini`; this should have {package}
|
57
|
+
placeholder in the string which will be resolved before execution
|
58
|
+
:param docker_cmd: the podman/docker executable to use
|
59
|
+
:param conf: the :class:`StaticConfiguration` for the container
|
60
|
+
:param runtime_conf: the `RuntimeConfiguration` of the container
|
61
|
+
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
62
|
+
:param dep_msg: if this is invoked for uninstalling a dependency then the string "dependency "
|
63
|
+
to display in messages, defaults to ""
|
64
|
+
:return: exit code of the underlying package manager command run using podman/docker
|
65
|
+
"""
|
66
|
+
installed = False
|
67
|
+
code, inst_packages = check_package(docker_cmd, check_cmd, package, conf.box_name)
|
68
|
+
if code == 0:
|
69
|
+
installed = True
|
70
|
+
if len(inst_packages) > 1:
|
71
|
+
print_notice(f"Multiple packages found for '{package}', select one to uninstall")
|
72
|
+
if selected_pkg := select_item_from_menu(inst_packages):
|
73
|
+
package = selected_pkg
|
74
|
+
else:
|
75
|
+
return 1
|
76
|
+
else:
|
77
|
+
package = inst_packages[0]
|
78
|
+
print_info(f"Uninstalling {dep_msg}'{package}' from '{conf.box_name}'")
|
79
|
+
code = int(run_command(build_shell_command(
|
80
|
+
docker_cmd, conf.box_name, uninstall_cmd.format(package=package)),
|
81
|
+
exit_on_error=False, error_msg=f"uninstalling '{package}'"))
|
82
|
+
elif not dep_msg: # dependency may have been uninstalled in original package uninstallation
|
83
|
+
print_error(f"Package '{package}' is not installed in container '{conf.box_name}'")
|
84
|
+
# go ahead with removal from local state and wrappers, even if package was not installed
|
85
|
+
if code == 0 or not installed:
|
86
|
+
orphans = state.unregister_package(conf.box_name, package, runtime_conf.shared_root)
|
87
|
+
if not skip_deps and orphans:
|
88
|
+
print_info(f"Uninstalling orphaned dependencies of '{package}' {list(orphans.keys())}")
|
89
|
+
for opt_dep in orphans:
|
90
|
+
_uninstall_package(opt_dep, skip_deps, uninstall_cmd, check_cmd, docker_cmd, conf,
|
91
|
+
runtime_conf, state, dep_msg="dependency ")
|
92
|
+
return code
|
ybox/pkg/update.py
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
"""
|
2
|
+
Update some or all packages on an active ybox container.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
from configparser import SectionProxy
|
7
|
+
|
8
|
+
from ybox.cmd import PkgMgr, build_shell_command, run_command
|
9
|
+
from ybox.config import StaticConfiguration
|
10
|
+
from ybox.print import print_warn
|
11
|
+
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
|
+
|
13
|
+
# TODO: updating packages can lead to system libraries among others getting updated. At the very
|
14
|
+
# least, the container may need to be restarted thereafter. All other containers on the same
|
15
|
+
# shared root should also be restarted which can be an issue for the user. This is also
|
16
|
+
# a problem when creating a new container on the same shared root since that too does update.
|
17
|
+
# The biggest problem can be that even a new package install can end up updating shared libs.
|
18
|
+
|
19
|
+
|
20
|
+
def update_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
21
|
+
conf: StaticConfiguration, runtime_conf: RuntimeConfiguration,
|
22
|
+
state: YboxStateManagement) -> int:
|
23
|
+
"""
|
24
|
+
Update the mentioned package installed in a container which can include packages not managed
|
25
|
+
by `ybox-pkg`, as well as those installed by other containers if the container shares the
|
26
|
+
same root directory with other containers.
|
27
|
+
|
28
|
+
When no package name is provided, then the entire installation is updated. Like above, it will
|
29
|
+
end up updating all the containers sharing the same root directory, if any. Note that
|
30
|
+
some rolling distributions like Arch Linux recommend always doing a full installation upgrade
|
31
|
+
rather than individual packages.
|
32
|
+
|
33
|
+
:param args: arguments having `packages` and all other attributes passed by the user
|
34
|
+
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
35
|
+
:param docker_cmd: the podman/docker executable to use
|
36
|
+
:param conf: the :class:`StaticConfiguration` for the container
|
37
|
+
:param runtime_conf: the `RuntimeConfiguration` of the container
|
38
|
+
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
39
|
+
:return: integer exit status of update command where 0 represents success
|
40
|
+
"""
|
41
|
+
quiet_flag = pkgmgr[PkgMgr.QUIET_FLAG.value] if args.quiet else ""
|
42
|
+
packages: list[str] = args.packages
|
43
|
+
if packages:
|
44
|
+
update_meta_cmd = pkgmgr[PkgMgr.UPDATE_META.value]
|
45
|
+
update_pkgs_cmd = pkgmgr[PkgMgr.UPDATE.value].format(quiet=quiet_flag,
|
46
|
+
packages=' '.join(packages))
|
47
|
+
update_cmd = f"{{ {update_meta_cmd}; }} && {{ {update_pkgs_cmd}; }}"
|
48
|
+
else:
|
49
|
+
update_cmd = pkgmgr[PkgMgr.UPDATE_ALL.value].format(quiet=quiet_flag)
|
50
|
+
if shared_containers := state.get_other_shared_containers(conf.box_name,
|
51
|
+
runtime_conf.shared_root):
|
52
|
+
# show all the containers sharing the same shared root
|
53
|
+
print_warn("The operation will also update packages in other containers having the same "
|
54
|
+
f"shared root directory: {', '.join(shared_containers)}")
|
55
|
+
return int(run_command(build_shell_command(docker_cmd, conf.box_name, update_cmd),
|
56
|
+
exit_on_error=False, error_msg="updating packages"))
|
ybox/print.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
"""
|
2
|
+
Utility classes and methods to print in color on terminal/console.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import shutil
|
7
|
+
import sys
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from typing import IO, Optional
|
10
|
+
|
11
|
+
|
12
|
+
# define color names for printing in terminal
|
13
|
+
@dataclass(frozen=True)
|
14
|
+
class TermColors:
|
15
|
+
"""basic ASCII color strings for terminals"""
|
16
|
+
black: str
|
17
|
+
red: str
|
18
|
+
green: str
|
19
|
+
orange: str
|
20
|
+
blue: str
|
21
|
+
purple: str
|
22
|
+
cyan: str
|
23
|
+
lightgray: str
|
24
|
+
reset: str
|
25
|
+
bold: str
|
26
|
+
disable: str
|
27
|
+
|
28
|
+
|
29
|
+
# foreground colors in the terminal
|
30
|
+
fgcolor = TermColors(
|
31
|
+
"\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m",
|
32
|
+
"\033[37m", "\033[00m", "\033[01m", "\033[02m")
|
33
|
+
# background colors in the terminal
|
34
|
+
bgcolor = TermColors(
|
35
|
+
"\033[40m", "\033[41m", "\033[42m", "\033[43m", "\033[44m", "\033[45m", "\033[46m",
|
36
|
+
"\033[47m", "\033[00m", "\033[01m", "\033[02m")
|
37
|
+
|
38
|
+
|
39
|
+
def get_terminal_width() -> int:
|
40
|
+
"""
|
41
|
+
Get the best estimate of the width of the current terminal.
|
42
|
+
This may not work well if the output is piped, for example.
|
43
|
+
"""
|
44
|
+
# Use stderr for the terminal width since stdout can be piped to pager.
|
45
|
+
try:
|
46
|
+
return os.get_terminal_size(sys.stderr.fileno()).columns
|
47
|
+
except OSError:
|
48
|
+
return shutil.get_terminal_size().columns
|
49
|
+
|
50
|
+
|
51
|
+
def print_color(msg: str, fg: Optional[str] = None,
|
52
|
+
bg: Optional[str] = None, end: str = "\n", file: Optional[IO[str]] = None) -> None:
|
53
|
+
"""
|
54
|
+
Display given string to standard output with foreground and background colors, if provided.
|
55
|
+
The colors will show up as expected on most known Linux terminals and console though
|
56
|
+
some colors may look different on different terminal implementation
|
57
|
+
(e.g. orange could be more like yellow).
|
58
|
+
|
59
|
+
:param msg: the string to be displayed
|
60
|
+
:param fg: the foreground color of the string
|
61
|
+
:param bg: the background color of the string
|
62
|
+
:param end: the terminating string which is newline by default (or can be empty for example)
|
63
|
+
:param file: the text-mode file object to use for writing (defaults to `sys.stdout`)
|
64
|
+
"""
|
65
|
+
if fg:
|
66
|
+
if bg:
|
67
|
+
full_msg = f"{fg}{bg}{msg}{bgcolor.reset}{fgcolor.reset}"
|
68
|
+
else:
|
69
|
+
full_msg = f"{fg}{msg}{fgcolor.reset}"
|
70
|
+
elif bg:
|
71
|
+
full_msg = f"{bg}{msg}{bgcolor.reset}"
|
72
|
+
else:
|
73
|
+
full_msg = msg
|
74
|
+
# force flush the output if it doesn't end in a newline
|
75
|
+
print(full_msg, end=end, file=file, flush=end != "\n")
|
76
|
+
|
77
|
+
|
78
|
+
def print_error(msg: str, end: str = "\n", file: Optional[IO[str]] = None) -> None:
|
79
|
+
"""
|
80
|
+
Display an error string in red foreground (and no background change).
|
81
|
+
|
82
|
+
:param msg: the string to be displayed
|
83
|
+
:param end: the terminating string which is newline by default (or can be empty for example)
|
84
|
+
:param file: the text-mode file object to use for writing (defaults to `sys.stderr`)
|
85
|
+
"""
|
86
|
+
if not file:
|
87
|
+
file = sys.stderr
|
88
|
+
print_color(msg, fg=fgcolor.red, end=end, file=file)
|
89
|
+
|
90
|
+
|
91
|
+
def print_warn(msg: str, end: str = "\n", file: Optional[IO[str]] = None):
|
92
|
+
"""
|
93
|
+
Display a warning string in purple foreground (and no background change).
|
94
|
+
|
95
|
+
:param msg: the string to be displayed
|
96
|
+
:param end: the terminating string which is newline by default (or can be empty for example)
|
97
|
+
:param file: the text-mode file object to use for writing (defaults to `sys.stdout`)
|
98
|
+
"""
|
99
|
+
print_color(msg, fg=fgcolor.purple, end=end, file=file)
|
100
|
+
|
101
|
+
|
102
|
+
def print_notice(msg: str, end: str = "\n", file: Optional[IO[str]] = None):
|
103
|
+
"""
|
104
|
+
Display a string in orange foreground (and no background change).
|
105
|
+
|
106
|
+
:param msg: the string to be displayed
|
107
|
+
:param end: the terminating string which is newline by default (or can be empty for example)
|
108
|
+
:param file: the text-mode file object to use for writing (defaults to `sys.stdout`)
|
109
|
+
"""
|
110
|
+
print_color(msg, fg=fgcolor.orange, end=end, file=file)
|
111
|
+
|
112
|
+
|
113
|
+
def print_info(msg: str, end: str = "\n", file: Optional[IO[str]] = None):
|
114
|
+
"""
|
115
|
+
Display an informational string in blue foreground (and no background change).
|
116
|
+
|
117
|
+
:param msg: the string to be displayed
|
118
|
+
:param end: the terminating string which is newline by default (or can be empty for example)
|
119
|
+
:param file: the text-mode file object to use for writing (defaults to `sys.stdout`)
|
120
|
+
"""
|
121
|
+
print_color(msg, fg=fgcolor.blue, end=end, file=file)
|
ybox/run/__init__.py
ADDED
File without changes
|
ybox/run/cmd.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
"""
|
2
|
+
Code for the `ybox-cmd` script that is used to execute programs in an active ybox container.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import sys
|
7
|
+
|
8
|
+
from ybox.cmd import run_command
|
9
|
+
from ybox.env import get_docker_command
|
10
|
+
|
11
|
+
|
12
|
+
def main() -> None:
|
13
|
+
"""main function for `ybox-cmd` script"""
|
14
|
+
main_argv(sys.argv[1:])
|
15
|
+
|
16
|
+
|
17
|
+
def main_argv(argv: list[str]) -> None:
|
18
|
+
"""
|
19
|
+
Main entrypoint of `ybox-cmd` that takes a list of arguments which are usually the
|
20
|
+
command-line arguments of the `main()` function. Pass ["-h"]/["--help"] to see all the
|
21
|
+
available arguments with help message for each.
|
22
|
+
|
23
|
+
:param argv: arguments to the function (main function passes `sys.argv[1:]`)
|
24
|
+
"""
|
25
|
+
args = parse_args(argv)
|
26
|
+
docker_cmd = get_docker_command()
|
27
|
+
container_name = args.container_name
|
28
|
+
|
29
|
+
docker_args = [docker_cmd, "exec"]
|
30
|
+
if not args.skip_terminal:
|
31
|
+
docker_args.append("-it")
|
32
|
+
docker_args.append(container_name)
|
33
|
+
if isinstance(args.command, str):
|
34
|
+
docker_args.append(args.command)
|
35
|
+
else:
|
36
|
+
docker_args.extend(args.command)
|
37
|
+
run_command(docker_args, error_msg=f"{args.command} execution on '{container_name}'")
|
38
|
+
|
39
|
+
|
40
|
+
def parse_args(argv: list[str]) -> argparse.Namespace:
|
41
|
+
"""
|
42
|
+
Parse command-line arguments for the program and return the result :class:`argparse.Namespace`.
|
43
|
+
|
44
|
+
:param argv: the list of arguments to be parsed
|
45
|
+
:return: the result of parsing using the `argparse` library as a :class:`argparse.Namespace`
|
46
|
+
"""
|
47
|
+
parser = argparse.ArgumentParser(description="Run a command on an active ybox container")
|
48
|
+
parser.add_argument("-s", "--skip-terminal", action="store_true",
|
49
|
+
help="skip interactive pseudo-terminal for the command "
|
50
|
+
"(i.e. skip -it options to podman/docker)")
|
51
|
+
parser.add_argument("container_name", type=str, help="name of the active ybox")
|
52
|
+
parser.add_argument("command", nargs="*", default="/bin/bash",
|
53
|
+
help="run the given command (default is /bin/bash)")
|
54
|
+
return parser.parse_args(argv)
|
ybox/run/control.py
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
"""
|
2
|
+
Code for the `ybox-control` script that is used to start/stop/restart a ybox container.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import sys
|
7
|
+
import time
|
8
|
+
|
9
|
+
from ybox.cmd import check_active_ybox, get_ybox_state, run_command
|
10
|
+
from ybox.config import StaticConfiguration
|
11
|
+
from ybox.env import Environ, get_docker_command
|
12
|
+
from ybox.print import fgcolor, print_color, print_error
|
13
|
+
from ybox.util import wait_for_ybox_container
|
14
|
+
|
15
|
+
|
16
|
+
def main() -> None:
|
17
|
+
"""main function for `ybox-control` script"""
|
18
|
+
main_argv(sys.argv[1:])
|
19
|
+
|
20
|
+
|
21
|
+
def start_container(docker_cmd: str, container_name: str):
|
22
|
+
"""
|
23
|
+
Start an existing ybox container.
|
24
|
+
|
25
|
+
:param docker_cmd: the podman/docker executable to use
|
26
|
+
:param container_name: name of the container
|
27
|
+
"""
|
28
|
+
if status := get_ybox_state(docker_cmd, container_name, (), exit_on_error=False):
|
29
|
+
if status[0] == "running":
|
30
|
+
print_color(f"Ybox container '{container_name}' already active", fg=fgcolor.cyan)
|
31
|
+
else:
|
32
|
+
print_color(f"Starting ybox container '{container_name}'", fg=fgcolor.cyan)
|
33
|
+
run_command([docker_cmd, "container", "start", container_name],
|
34
|
+
error_msg="container start")
|
35
|
+
conf = StaticConfiguration(Environ(docker_cmd), status[1], container_name)
|
36
|
+
wait_for_ybox_container(docker_cmd, conf)
|
37
|
+
else:
|
38
|
+
print_error(f"No ybox container '{container_name}' found")
|
39
|
+
sys.exit(1)
|
40
|
+
|
41
|
+
|
42
|
+
def stop_container(docker_cmd: str, container_name: str, fail_on_error: bool):
|
43
|
+
"""
|
44
|
+
Stop a ybox container.
|
45
|
+
|
46
|
+
:param docker_cmd: the podman/docker executable to use
|
47
|
+
:param container_name: name of the container
|
48
|
+
:param fail_on_error: if True then show error message on failure to stop else ignore
|
49
|
+
"""
|
50
|
+
if check_active_ybox(docker_cmd, container_name):
|
51
|
+
print_color(f"Stopping ybox container '{container_name}'", fg=fgcolor.cyan)
|
52
|
+
run_command([docker_cmd, "container", "stop", container_name], error_msg="container stop")
|
53
|
+
for _ in range(120):
|
54
|
+
time.sleep(0.5)
|
55
|
+
if get_ybox_state(docker_cmd, container_name, ("exited", "stopped"),
|
56
|
+
exit_on_error=False, state_msg=" stopped"):
|
57
|
+
return
|
58
|
+
print_error(f"Failed to stop ybox container '{container_name}'")
|
59
|
+
elif fail_on_error:
|
60
|
+
print_error(f"No active ybox container '{container_name}' found")
|
61
|
+
sys.exit(1)
|
62
|
+
else:
|
63
|
+
print_color(f"No active ybox container '{container_name}' found", fg=fgcolor.cyan)
|
64
|
+
|
65
|
+
|
66
|
+
def main_argv(argv: list[str]) -> None:
|
67
|
+
"""
|
68
|
+
Main entrypoint of `ybox-control` that takes a list of arguments which are usually the
|
69
|
+
command-line arguments of the `main()` function. Pass ["-h"]/["--help"] to see all the
|
70
|
+
available arguments with help message for each.
|
71
|
+
|
72
|
+
:param argv: arguments to the function (main function passes `sys.argv[1:]`)
|
73
|
+
"""
|
74
|
+
args = parse_args(argv)
|
75
|
+
docker_cmd = get_docker_command()
|
76
|
+
container_name = args.container_name
|
77
|
+
if args.action == "start":
|
78
|
+
start_container(docker_cmd, container_name)
|
79
|
+
elif args.action == "stop":
|
80
|
+
stop_container(docker_cmd, container_name, fail_on_error=True)
|
81
|
+
elif args.action == "restart":
|
82
|
+
stop_container(docker_cmd, container_name, fail_on_error=False)
|
83
|
+
start_container(docker_cmd, container_name)
|
84
|
+
elif args.action == "status":
|
85
|
+
if status := get_ybox_state(docker_cmd, container_name, (), exit_on_error=False):
|
86
|
+
print(status[0])
|
87
|
+
else:
|
88
|
+
print_error(f"No ybox container '{container_name}' found")
|
89
|
+
|
90
|
+
|
91
|
+
def parse_args(argv: list[str]) -> argparse.Namespace:
|
92
|
+
"""
|
93
|
+
Parse command-line arguments for the program and return the result :class:`argparse.Namespace`.
|
94
|
+
|
95
|
+
:param argv: the list of arguments to be parsed
|
96
|
+
:return: the result of parsing using the `argparse` library as a :class:`argparse.Namespace`
|
97
|
+
"""
|
98
|
+
parser = argparse.ArgumentParser(description="control ybox containers")
|
99
|
+
parser.add_argument("action", choices=["start", "stop", "restart", "status"],
|
100
|
+
help="action to perform")
|
101
|
+
parser.add_argument("container_name", help="name of the ybox")
|
102
|
+
return parser.parse_args(argv)
|