-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Envs] Implement remote environment installation (pyenv, python)
- Loading branch information
Showing
6 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import importlib.resources as pkg_resources | ||
|
||
from fabric.connection import Connection | ||
|
||
import conductor.envs.resources as env_resources | ||
from conductor.config import MAESTRO_ROOT, MAESTRO_PYTHON_VERSION | ||
from conductor.errors import MaestroInstallError | ||
|
||
|
||
def install_maestro(c: Connection) -> None: | ||
""" | ||
Installs Maestro in the remote environment connected to by `c`. | ||
""" | ||
# General strategy: | ||
# - Keep all Maestro-related code under a special root directory, specified | ||
# using `MAESTRO_ROOT`. The assumption is that `MAESTRO_ROOT` is stored | ||
# under `$HOME` (we assume a Unix-like remote environment). | ||
# - Rely on `pyenv` to install a compatible version of Python in the remote | ||
# environment. Note that `pyenv` depends on `git`. | ||
# - Copy the entire Conductor module into the remote environment and install | ||
# its dependencies in the pyenv. | ||
# | ||
# Design goals: | ||
# - We should make this install as seamless as possible, similar to how easy | ||
# it is to install VSCode server. | ||
# - OK to assume a Unix-like remote environment. Should ideally make sure | ||
# this works in a generic EC2 environment and inside a Docker container. | ||
|
||
try: | ||
installer = _MaestroInstaller.create(c) | ||
installer.ensure_root_dir() | ||
installer.ensure_pyenv_installed() | ||
installer.install_python() | ||
except Exception as ex: | ||
raise MaestroInstallError(error_msg=str(ex)) from ex | ||
|
||
|
||
class _MaestroInstaller: | ||
@classmethod | ||
def create(cls, c: Connection) -> "_MaestroInstaller": | ||
result = c.run("echo $HOME") | ||
home_dir = result.stdout.strip() | ||
return cls(c, home_dir) | ||
|
||
def __init__(self, c: Connection, home_dir: str) -> None: | ||
self._c = c | ||
# This is an absolute path in the remote environment. | ||
self._maestro_root = f"{home_dir}/{MAESTRO_ROOT}" | ||
self._pyenv_root = f"{self._maestro_root}/pyenv" | ||
|
||
def ensure_root_dir(self) -> None: | ||
self._c.run(f"mkdir -p {self._maestro_root}") | ||
|
||
def ensure_pyenv_installed(self) -> None: | ||
result = self._c.run(f"ls {self._pyenv_root}", warn=True, hide="both") | ||
if result.ok: | ||
# Assume install succeeded. | ||
return | ||
|
||
try: | ||
# Need to install. | ||
install_script = pkg_resources.files(env_resources).joinpath( | ||
"install_pyenv.sh" | ||
) | ||
with pkg_resources.as_file(install_script) as path: | ||
self._c.put(path, f"{self._maestro_root}/install_pyenv.sh") | ||
# We want a custom install path to avoid interfering with the existing environment. | ||
self._c.run( | ||
f"bash {self._maestro_root}/install_pyenv.sh", | ||
env={"PYENV_ROOT": self._pyenv_root}, | ||
hide="both", | ||
) | ||
except Exception: | ||
# If the installation failed, remove the directory so that we will | ||
# retry next time. | ||
self._c.run(f"rm -r {self._pyenv_root}") | ||
raise | ||
|
||
def install_python(self) -> None: | ||
self._c.run( | ||
f"{self._pyenv_root}/bin/pyenv install {MAESTRO_PYTHON_VERSION}", | ||
env={"PYENV_ROOT": self._pyenv_root}, | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Source: https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | ||
# Repository: https://github.com/pyenv/pyenv-installer | ||
# Commit hash: 86a08ac9e38ec3a267e4b5c758891caf1233a2e4 | ||
|
||
set -e | ||
[ -n "$PYENV_DEBUG" ] && set -x | ||
|
||
if [ -z "$PYENV_ROOT" ]; then | ||
if [ -z "$HOME" ]; then | ||
printf "$0: %s\n" \ | ||
"Either \$PYENV_ROOT or \$HOME must be set to determine the install location." \ | ||
>&2 | ||
exit 1 | ||
fi | ||
export PYENV_ROOT="${HOME}/.pyenv" | ||
fi | ||
|
||
colorize() { | ||
if [ -t 1 ]; then printf "\e[%sm%s\e[m" "$1" "$2" | ||
else echo -n "$2" | ||
fi | ||
} | ||
|
||
# Checks for `.pyenv` file, and suggests to remove it for installing | ||
if [ -d "${PYENV_ROOT}" ]; then | ||
{ echo | ||
colorize 1 "WARNING" | ||
echo ": Can not proceed with installation. Kindly remove the '${PYENV_ROOT}' directory first." | ||
echo | ||
} >&2 | ||
exit 1 | ||
fi | ||
|
||
failed_checkout() { | ||
echo "Failed to git clone $1" | ||
exit -1 | ||
} | ||
|
||
checkout() { | ||
[ -d "$2" ] || git -c advice.detachedHead=0 clone --branch "$3" --depth 1 "$1" "$2" || failed_checkout "$1" | ||
} | ||
|
||
if ! command -v git 1>/dev/null 2>&1; then | ||
echo "pyenv: Git is not installed, can't continue." >&2 | ||
exit 1 | ||
fi | ||
|
||
# Check ssh authentication if USE_SSH is present | ||
if [ -n "${USE_SSH}" ]; then | ||
if ! command -v ssh 1>/dev/null 2>&1; then | ||
echo "pyenv: configuration USE_SSH found but ssh is not installed, can't continue." >&2 | ||
exit 1 | ||
fi | ||
|
||
# `ssh -T [email protected]' returns 1 on success | ||
# See https://docs.github.com/en/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection | ||
ssh -T [email protected] 1>/dev/null 2>&1 || EXIT_CODE=$? | ||
if [[ ${EXIT_CODE} != 1 ]]; then | ||
echo "pyenv: github ssh authentication failed." | ||
echo | ||
echo "In order to use the ssh connection option, you need to have an ssh key set up." | ||
echo "Please generate an ssh key by using ssh-keygen, or follow the instructions at the following URL for more information:" | ||
echo | ||
echo "> https://docs.github.com/en/repositories/creating-and-managing-repositories/troubleshooting-cloning-errors#check-your-ssh-access" | ||
echo | ||
echo "Once you have an ssh key set up, try running the command again." | ||
exit 1 | ||
fi | ||
fi | ||
|
||
if [ -n "${USE_SSH}" ]; then | ||
GITHUB="[email protected]:" | ||
else | ||
GITHUB="https://github.com/" | ||
fi | ||
|
||
checkout "${GITHUB}pyenv/pyenv.git" "${PYENV_ROOT}" "${PYENV_GIT_TAG:-master}" | ||
checkout "${GITHUB}pyenv/pyenv-doctor.git" "${PYENV_ROOT}/plugins/pyenv-doctor" "master" | ||
checkout "${GITHUB}pyenv/pyenv-update.git" "${PYENV_ROOT}/plugins/pyenv-update" "master" | ||
checkout "${GITHUB}pyenv/pyenv-virtualenv.git" "${PYENV_ROOT}/plugins/pyenv-virtualenv" "master" | ||
|
||
if ! command -v pyenv 1>/dev/null; then | ||
{ echo | ||
colorize 1 "WARNING" | ||
echo ": seems you still have not added 'pyenv' to the load path." | ||
echo | ||
} >&2 | ||
|
||
{ # Without args, `init` commands print installation help | ||
"${PYENV_ROOT}/bin/pyenv" init || true | ||
"${PYENV_ROOT}/bin/pyenv" virtualenv-init || true | ||
} >&2 | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters