diff options
author | John Mulligan <jmulligan@redhat.com> | 2022-06-16 21:26:00 +0200 |
---|---|---|
committer | John Mulligan <jmulligan@redhat.com> | 2022-09-13 18:17:20 +0200 |
commit | 6ba6981148188572209cd950188a8da0e75ea766 (patch) | |
tree | 3c850db79bf9106710779945e2bd86019321896a /src/cephadm | |
parent | qa/tasks/cephadm: conditionally pull cephadm binary from chacra (diff) | |
download | ceph-6ba6981148188572209cd950188a8da0e75ea766.tar.xz ceph-6ba6981148188572209cd950188a8da0e75ea766.zip |
cephadm: add a pythonic build.py for constructing the cephadm binary
As discussed in a ceph orch meeting, the build.sh script was deemed
"unpythonic". This script is a first attempt to do it more pythonic
with fewer explicit version checks.
This change minimizes the existing build.sh to simply call build.py.
We can completely eliminate build.sh at a later time if desired.
Signed-off-by: John Mulligan <jmulligan@redhat.com>
Diffstat (limited to 'src/cephadm')
-rwxr-xr-x | src/cephadm/build.py | 158 | ||||
-rwxr-xr-x | src/cephadm/build.sh | 44 |
2 files changed, 159 insertions, 43 deletions
diff --git a/src/cephadm/build.py b/src/cephadm/build.py new file mode 100755 index 00000000000..4e97f5d3757 --- /dev/null +++ b/src/cephadm/build.py @@ -0,0 +1,158 @@ +#!/usr/bin/python3 +"""Build cephadm from one or more files into a standalone executable. +""" +# TODO: If cephadm is being built and packaged within a format such as RPM +# do we have to do anything special wrt passing in the version +# of python to build with? Even with the intermediate cmake layer? + +import argparse +import logging +import os +import pathlib +import shutil +import subprocess +import tempfile +import sys + +HAS_ZIPAPP = False +try: + import zipapp + + HAS_ZIPAPP = True +except ImportError: + pass + + +log = logging.getLogger(__name__) + + +def _reexec(python): + """Switch to the selected version of python by exec'ing into the desired + python path. + Sets the _BUILD_PYTHON_SET env variable as a sentinel to indicate exec has + been performed. + """ + env = os.environ.copy() + env["_BUILD_PYTHON_SET"] = python + os.execvpe(python, [python, __file__] + sys.argv[1:], env) + + +def _did_rexec(): + """Returns true if the process has already exec'ed into the desired python + version. + """ + return bool(os.environ.get("_BUILD_PYTHON_SET", "")) + + +def _build(dest, src): + """Build the binary.""" + os.chdir(src) + tempdir = pathlib.Path(tempfile.mkdtemp(suffix=".cephadm.build")) + log.debug("working in %s", tempdir) + try: + if os.path.isfile("requirements.txt"): + _install_deps(tempdir) + log.info("Copying contents") + # TODO: currently the only file relevant to a compiled cephadm is the + # cephadm.py file. Once cephadm is broken up into multiple py files + # (and possibly other libs from python-common, etc) we'll want some + # sort organized structure to track what gets copied into the + # dir to be zipped. For now we just have a simple call to copy + # (and rename) the one file we care about. + shutil.copy("cephadm.py", tempdir / "__main__.py") + _compile(dest, tempdir) + finally: + shutil.rmtree(tempdir) + + +def _compile(dest, tempdir): + """Compile the zipapp.""" + # TODO we could explicitly pass a python version here + log.info("Constructing the zipapp file") + try: + zipapp.create_archive( + source=tempdir, + target=dest, + interpreter=sys.executable, + compressed=True, + ) + log.info("Zipapp created with compression") + except TypeError: + # automatically fall back to uncompressed + zipapp.create_archive( + source=tempdir, + target=dest, + interpreter=sys.executable, + ) + log.info("Zipapp created without compression") + + +def _install_deps(tempdir): + """Install dependencies with pip.""" + # TODO we could explicitly pass a python version here + log.info("Installing dependencies") + # apparently pip doesn't have an API, just a cli. + subprocess.check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "--requirement", + "requirements.txt", + "--target", + tempdir, + ] + ) + + +def main(): + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("cephadm/build.py: %(message)s")) + log.addHandler(handler) + log.setLevel(logging.INFO) + + log.debug("argv: %r", sys.argv) + parser = argparse.ArgumentParser() + parser.add_argument( + "dest", help="Destination path name for new cephadm binary" + ) + parser.add_argument( + "--source", help="Directory containing cephadm sources" + ) + parser.add_argument( + "--python", help="The path to the desired version of python" + ) + args = parser.parse_args() + + if not _did_rexec() and args.python: + _reexec(args.python) + + log.info( + "Python Version: {v.major}.{v.minor}.{v.micro}".format( + v=sys.version_info + ) + ) + log.info("Args: %s", vars(args)) + if not HAS_ZIPAPP: + # Unconditionally display an error that the version of python + # lacks zipapp (probably too old). + print("error: zipapp module not found", file=sys.stderr) + print( + "(zipapp is available in Python 3.5 or later." + " are you using a new enough version?)", + file=sys.stderr, + ) + sys.exit(2) + if args.source: + source = pathlib.Path(args.source).absolute() + else: + source = pathlib.Path(__file__).absolute().parent + dest = pathlib.Path(args.dest).absolute() + log.info("Source Dir: %s", source) + log.info("Destination Path: %s", dest) + _build(dest, source) + + +if __name__ == "__main__": + main() diff --git a/src/cephadm/build.sh b/src/cephadm/build.sh index 00bd4c74925..84b58f14f57 100755 --- a/src/cephadm/build.sh +++ b/src/cephadm/build.sh @@ -1,47 +1,5 @@ #!/bin/bash -ex -SCRIPT_NAME=$(basename ${BASH_SOURCE[0]}) SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -clean_up() { - if [ -e ${buildir} ]; then - rm -rf ${builddir} - fi -} -trap clean_up EXIT - -PYTHON=$(which python3) - -# Create build directory and install required dependencies -target_fpath=${SCRIPT_DIR}/cephadm -if [ -n "$1" ]; then - target_fpath="$1" -fi -builddir=$(mktemp -d) -if [ -e "requirements.txt" ]; then - $PYTHON -m pip install -r requirements.txt --target ${builddir} -fi - -# Make sure all newly created source files are copied here as well! -cp ${SCRIPT_DIR}/cephadm.py ${builddir}/__main__.py - -version=$($PYTHON --version) -if [[ "$version" =~ ^Python[[:space:]]([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)$ ]]; then - major=${BASH_REMATCH[1]} - minor=${BASH_REMATCH[2]} - - compress="" - if [[ "$major" -ge 3 && "$minor" -ge 7 ]]; then - echo "Python version compatible with --compress, compressing cephadm binary" - compress="--compress" - elif [[ "$major" -lt 3 || "$major" -eq 3 && "$minor" -lt 5 ]]; then - echo "zipapp module requires Python35 or greater" - exit 1 - fi - - $PYTHON -mzipapp -p $PYTHON ${builddir} ${compress} --output $target_fpath - echo written to ${target_fpath} -else - echo "Couldn't parse Python version" - exit 1 -fi +exec python3 $SCRIPT_DIR/build.py "$@" |