diff --git a/src/workbench-jupyter-parabricks/.devcontainer.json b/src/workbench-jupyter-parabricks/.devcontainer.json new file mode 100644 index 00000000..c3b24a92 --- /dev/null +++ b/src/workbench-jupyter-parabricks/.devcontainer.json @@ -0,0 +1,51 @@ +{ + "name": "Workbench JupyterLab with docker support devcontainer template plus NVIDIA Parabricks", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "shutdownAction": "none", + "workspaceFolder": "/workspace", + // Get the host's docker group ID and propagate it into the .env file, which + // allows it to be used within docker-compose.yaml. + "initializeCommand": "DOCKER_GID=`getent group docker | cut -d: -f3` && echo \"DOCKER_GID=${DOCKER_GID}\" > .env", + "postCreateCommand": "./startupscript/post-startup.sh jupyter /home/jupyter ${templateOption:cloud} ${templateOption:login} && ./startupscript/setup-docker.sh", + // re-mount bucket files on container start up + "postStartCommand": [ + "./startupscript/remount-on-restart.sh", + "jupyter", + "/home/jupyter", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "remoteUser": "root", + "customizations": { + "workbench": { + "opens": { + "extensions": [ + // Source + ".ipynb", + ".R", + ".py", + // Documents + ".md", + ".html", + ".latex", + ".pdf", + // Images + ".bmp", + ".gif", + ".jpeg", + ".jpg", + ".png", + ".svg", + // Data + ".csv", + ".tsv", + ".json", + ".vl" + ], + "fileUrlSuffix": "/lab/tree/{path}", + "folderUrlSuffix": "/lab/tree/{path}" + } + } + } +} diff --git a/src/workbench-jupyter-parabricks/Dockerfile b/src/workbench-jupyter-parabricks/Dockerfile new file mode 100644 index 00000000..c4d49c15 --- /dev/null +++ b/src/workbench-jupyter-parabricks/Dockerfile @@ -0,0 +1,21 @@ +# Use the official NVIDIA Clara Parabricks container as the base image +FROM nvcr.io/nvidia/clara/clara-parabricks:4.5.1-1 + +# Set the working directory inside the container +WORKDIR /workspace + +# Switch to root user to install packages +USER root + +# Update package lists and install python3, pip, and other essentials +# Clean up apt-get cache to reduce image size +RUN apt-get update && \ + apt-get install -y python3 python3-pip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Jupyter Lab using pip +RUN pip3 install jupyterlab + +# Expose the port Jupyter Lab will run on +EXPOSE 8888 \ No newline at end of file diff --git a/src/workbench-jupyter-parabricks/devcontainer-template.json b/src/workbench-jupyter-parabricks/devcontainer-template.json new file mode 100644 index 00000000..b844bdb0 --- /dev/null +++ b/src/workbench-jupyter-parabricks/devcontainer-template.json @@ -0,0 +1,23 @@ +{ + "id": "custom-workbench-jupyter-parabricks-template", + "description": "A template used to serve the Workbench JupyterLab container image plus NVIDIA Parabricks", + "version": "0.0.1", + "name": "Workbench Prebuilt JupyterLab Template running alongside NVIDIA Parabricks", + "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/custom-workbench-jupyter-template", + "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", + "options": { + "cloud": { + "type": "string", + "description": "VM cloud environment", + "proposals": ["gcp", "aws"], + "default": "gcp" + }, + "login": { + "type": "string", + "description": "Whether to log in to workbench CLI", + "proposals": ["true", "false"], + "default": "false" + } + }, + "platforms": ["Any"] +} diff --git a/src/workbench-jupyter-parabricks/docker-compose.yaml b/src/workbench-jupyter-parabricks/docker-compose.yaml new file mode 100644 index 00000000..cd279932 --- /dev/null +++ b/src/workbench-jupyter-parabricks/docker-compose.yaml @@ -0,0 +1,23 @@ +version: "2.4" +services: + app: + container_name: "application-server" + image: "us-central1-docker.pkg.dev/wb-quick-seed-877/parabricks-demo-repo/test1:20250827" + user: root + restart: always + volumes: + - .:/workspace:cached + ports: + - "8888:8888" + networks: + - app-network + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse + security_opt: + - apparmor:unconfined + command: jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --allow-root --LabApp.token='' +networks: + app-network: + external: true \ No newline at end of file diff --git a/startupscript/gcp/resource-mount.sh b/startupscript/gcp/resource-mount.sh index 78d231a5..5d85b0ff 100644 --- a/startupscript/gcp/resource-mount.sh +++ b/startupscript/gcp/resource-mount.sh @@ -50,5 +50,5 @@ fi sed -i '/user_allow_other/s/^#//g' /etc/fuse.conf if [[ "${LOG_IN}" == "true" ]]; then - ${RUN_AS_LOGIN_USER} "wb resource mount --allow-other || echo 'Resource mounting failed.'" + ${RUN_AS_LOGIN_USER} "/'${WORKBENCH_INSTALL_PATH}' resource mount --allow-other || echo 'Resource mounting failed.'" fi diff --git a/startupscript/git-setup.sh b/startupscript/git-setup.sh index a5e178ae..72ef017b 100755 --- a/startupscript/git-setup.sh +++ b/startupscript/git-setup.sh @@ -31,7 +31,7 @@ ${RUN_AS_LOGIN_USER} "mkdir -p ${USER_SSH_DIR} --mode 0700" # Get the user's SSH key from Workbench, and if set, write it to the user's .ssh directory ${RUN_AS_LOGIN_USER} "\ install --mode 0600 /dev/null '${USER_SSH_DIR}/id_rsa.tmp' && \ - wb security ssh-key get --include-private-key --format=JSON >> '${USER_SSH_DIR}/id_rsa.tmp' || true" + '${WORKBENCH_INSTALL_PATH}' security ssh-key get --include-private-key --format=JSON >> '${USER_SSH_DIR}/id_rsa.tmp' || true" if [[ -s "${USER_SSH_DIR}/id_rsa.tmp" ]]; then ${RUN_AS_LOGIN_USER} "\ install --mode 0600 /dev/null '${USER_SSH_DIR}/id_rsa' && \ @@ -63,7 +63,7 @@ ${RUN_AS_LOGIN_USER} "mkdir -p '${WORKBENCH_GIT_REPOS_DIR}'" # shellcheck disable=SC2164 pushd "${WORKBENCH_GIT_REPOS_DIR}" -${RUN_AS_LOGIN_USER} "wb resource list --type=GIT_REPO --format json" | \ +${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' resource list --type=GIT_REPO --format json" | \ jq -c .[] | \ while read -r ITEM; do GIT_REPO_NAME="$(echo "$ITEM" | jq -r .id)" diff --git a/startupscript/install-cli.sh b/startupscript/install-cli.sh index bd982b1a..b98fa79c 100755 --- a/startupscript/install-cli.sh +++ b/startupscript/install-cli.sh @@ -44,7 +44,7 @@ fi readonly TERRA_SERVER # Only install cli if not already installed -if ! command -v wb &> /dev/null; then +if ! command -v '${WORKBENCH_INSTALL_PATH}'&> /dev/null; then emit "Installing the Workbench CLI ..." if ! AXON_VERSION_URL="$(get_axon_version_url "${TERRA_SERVER}")"; then @@ -69,21 +69,21 @@ if ! command -v wb &> /dev/null; then cp wb "${WORKBENCH_INSTALL_PATH}" # Copy 'wb' to its legacy 'terra' name. - cp "${WORKBENCH_INSTALL_PATH}" "${WORKBENCH_LEGACY_PATH}" + cp wb "${WORKBENCH_LEGACY_PATH}" fi # Set browser manual login since that's the only login supported from a Vertex AI Notebook VM -${RUN_AS_LOGIN_USER} "wb config set browser MANUAL" +${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' config set browser MANUAL" # Set the CLI server based on the server that created the VM. -${RUN_AS_LOGIN_USER} "wb server set --name=${TERRA_SERVER}" +${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' server set --name=${TERRA_SERVER}" # Generate the bash completion script -${RUN_AS_LOGIN_USER} "wb generate-completion > '${USER_BASH_COMPLETION_DIR}/workbench'" +${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' generate-completion > '${USER_BASH_COMPLETION_DIR}/workbench'" if [[ "${LOG_IN}" == "true" ]]; then - # For GCP use "APP_DEFAULT_CREDENTIALS", for AWS use "AWS_IAM" as --mode arg to "wb auth login". + # For GCP use "APP_DEFAULT_CREDENTIALS", for AWS use "AWS_IAM" as --mode arg to "/usr/bin/wb auth login". LOG_IN_MODE="APP_DEFAULT_CREDENTIALS" if [[ "${CLOUD}" == "aws" ]]; then LOG_IN_MODE="AWS_IAM" @@ -92,13 +92,13 @@ if [[ "${LOG_IN}" == "true" ]]; then # Log in with app-default-credentials emit "Logging into workbench CLI with mode ${LOG_IN_MODE}" - ${RUN_AS_LOGIN_USER} "wb auth login --mode=${LOG_IN_MODE}" + ${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' auth login --mode=${LOG_IN_MODE}" # Set the CLI workspace id using the VM metadata, if set. TERRA_WORKSPACE="$(get_metadata_value "terra-workspace-id")" readonly TERRA_WORKSPACE if [[ -n "${TERRA_WORKSPACE}" ]]; then - ${RUN_AS_LOGIN_USER} "wb workspace set --id='${TERRA_WORKSPACE}'" + ${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' workspace set --id='${TERRA_WORKSPACE}'" fi else emit "Do not log user into workbench CLI. Manual log in is required." diff --git a/startupscript/remount-on-restart.sh b/startupscript/remount-on-restart.sh index 610a28c9..bdd5dd23 100755 --- a/startupscript/remount-on-restart.sh +++ b/startupscript/remount-on-restart.sh @@ -49,8 +49,8 @@ source "${SCRIPT_DIR}/emit.sh" # CLI login ############################# readonly RUN_AS_LOGIN_USER="sudo -u ${USER_NAME} bash -l -c" -if [[ "${LOG_IN}" == "true" ]] && ${RUN_AS_LOGIN_USER} "wb auth status 2>&1" | grep -q "NO USER LOGGED IN"; then - ${RUN_AS_LOGIN_USER} "wb auth login --mode=APP_DEFAULT_CREDENTIALS" +if [[ "${LOG_IN}" == "true" ]] && ${RUN_AS_LOGIN_USER} "/usr/bin/wb auth status 2>&1" | grep -q "NO USER LOGGED IN"; then + ${RUN_AS_LOGIN_USER} "/usr/bin/wb auth login --mode=APP_DEFAULT_CREDENTIALS" fi ############################# diff --git a/startupscript/setup-bashrc.sh b/startupscript/setup-bashrc.sh index 5062bddf..8a9a9754 100644 --- a/startupscript/setup-bashrc.sh +++ b/startupscript/setup-bashrc.sh @@ -27,14 +27,14 @@ emit "Customize user bashrc ..." if [[ "${LOG_IN}" == "true" ]]; then # OWNER_EMAIL is really the Workbench user account email address OWNER_EMAIL="$( - ${RUN_AS_LOGIN_USER} "wb workspace describe --format=json" | \ + ${RUN_AS_LOGIN_USER} "''${WORKBENCH_INSTALL_PATH}'' workspace describe --format=json" | \ jq --raw-output ".userEmail")" readonly OWNER_EMAIL # PET_SA_EMAIL is the pet service account for the Workbench user and # is specific to the GCP project backing the workspace PET_SA_EMAIL="$( - ${RUN_AS_LOGIN_USER} "wb auth status --format=json" | \ + ${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' auth status --format=json" | \ jq --raw-output ".serviceAccountEmail")" readonly PET_SA_EMAIL @@ -57,7 +57,7 @@ if [[ "${CLOUD}" == "gcp" && "${LOG_IN}" == "true" ]]; then # GOOGLE_PROJECT is the project id for the GCP project backing the workspace GOOGLE_PROJECT="$( - ${RUN_AS_LOGIN_USER} "wb workspace describe --format=json" | \ + ${RUN_AS_LOGIN_USER} "'${WORKBENCH_INSTALL_PATH}' workspace describe --format=json" | \ jq --raw-output ".googleProjectId")" readonly GOOGLE_PROJECT @@ -77,7 +77,7 @@ if [[ "${CLOUD}" == "aws" && "${LOG_IN}" == "true" ]]; then # Create a symlink to this workspace's AWS config file to use as the target for AWS_CONFIG_FILE. readonly AWS_CONFIG_SYMLINK="${USER_WORKBENCH_CONFIG_DIR}/workspace.conf" - ${RUN_AS_LOGIN_USER} "eval \$(wb workspace configure-aws) && \ + ${RUN_AS_LOGIN_USER} "eval \$('${WORKBENCH_INSTALL_PATH}' workspace configure-aws) && \ ln -sf \${AWS_CONFIG_FILE} ${AWS_CONFIG_SYMLINK}" emit "Adding Workbench AWS-sepcific environment variables to ~/.bashrc ..."