From 322ac756e84417f1c9c6b182cfcc709c54fbb4bc Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 29 Jul 2022 17:14:41 -0400 Subject: [PATCH 01/26] FROM ubuntu:20.04 --- terra-jupyter-dev-base/Dockerfile | 157 +++++++++++++++++++++++++ terra-jupyter-dev-base/build_docker.sh | 3 + 2 files changed, 160 insertions(+) create mode 100644 terra-jupyter-dev-base/Dockerfile create mode 100755 terra-jupyter-dev-base/build_docker.sh diff --git a/terra-jupyter-dev-base/Dockerfile b/terra-jupyter-dev-base/Dockerfile new file mode 100644 index 00000000..2050f5d2 --- /dev/null +++ b/terra-jupyter-dev-base/Dockerfile @@ -0,0 +1,157 @@ +FROM ubuntu:20.04 + +USER root + +####################### +# Prerequisites +####################### +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && apt-get install -yq --no-install-recommends sudo \ + && sudo -i \ + echo "deb http://security.ubuntu.com/ubuntu/ bionic main" >> /etc/apt/sources.list \ + sudo apt update libexempi3 \ + && sudo -i \ + echo "deb http://us.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list \ + sudo apt update libv8-3.14-dev + +RUN apt-get update && apt-get install -yq --no-install-recommends \ + # gnupg requirement + dirmngr \ + gnupg \ + # curl requirement + curl \ + ca-certificates \ + # useful utilities for debugging within the docker + nano \ + procps \ + lsb-release \ + # python requirements + checkinstall \ + build-essential \ + zlib1g-dev \ + # pip requirements + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + libexempi3 \ + libv8-3.14-dev \ + # install script requirements + sudo \ + locales \ + # for ssh-agent and ssh-add + keychain \ + # openjdk 11 + default-jre \ + default-jdk \ + # git + git \ +# Uncomment en_US.UTF-8 for inclusion in generation + && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ + # Generate locale + && locale-gen \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ENV LC_ALL en_US.UTF-8 + +####################### +# Python / Jupyter +####################### + +ENV USER jupyter +RUN useradd -m -s /bin/bash $USER \ + && usermod -g users $USER +ENV HOME /home/$USER + +# The welder uid is consistent with the Welder docker definition here: +# https://github.com/DataBiosphere/welder/blob/master/project/Settings.scala +# Adding welder-user to the Jupyter container isn't strictly required, but it makes welder-added +# files display nicer when viewed in a terminal. +ENV WELDER_USER welder-user +ENV WELDER_UID 1001 +RUN useradd -m -s /bin/bash -N -u $WELDER_UID $WELDER_USER + +# ensure this matches c.NotebookApp.port in jupyter_notebook_config.py +ENV JUPYTER_PORT 8000 +ENV JUPYTER_HOME /etc/jupyter + +# install miniconda to /opt/conda +ENV CONDA_AUTO_UPDATE_CONDA=false +ENV PATH="${PATH}:/opt/conda/bin:${HOME}/.local/bin:${HOME}/packages/bin" +RUN curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ + && chmod +x $HOME/miniconda.sh \ + && $HOME/miniconda.sh -b -p /opt/conda \ + && rm $HOME/miniconda.sh + +RUN pip3 -V \ + # For gcloud alpha storage support. + && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ + && git clone --branch sf_missing_requirement --single-branch \ + https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ + # tmp hack min-5 + # I'm not installing jupyterlab and I can't update init-actions.sh to not access it + && mkdir -p /usr/local/share/jupyter/lab \ + # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. + # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) + # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this + && pip3 install notebook \ + && pip3 install ipykernel \ + && pip3 install python-datauri \ + && pip3 install jupyter_contrib_nbextensions \ + && pip3 install jupyter_nbextensions_configurator \ + && pip3 install markupsafe==2.0.1 \ + # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624 + && pip3 install "nbconvert>=6.4.5" \ + # for jupyter_delocalize.py and jupyter_notebook_config.py + && pip3 install requests \ + && pip3 install firecloud \ + && pip3 install terra-notebook-utils \ + && pip3 install crcmod \ + # install Cromshell 2.0, using repo cloned above + && pip install $HOME/cromshell + +# make pip install to a user directory, instead of a system directory which requires root. +# this is useful so `pip install` commands can be run in the context of a notebook. +ENV PIP_USER=true +# When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. +# Append '/home/jupyter/.local/bin' to PATH +# pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user +#ENV PATH="${PATH}:${HOME}/.local/bin:${HOME}/packages/bin" + +####################### +# Utilities +####################### + +COPY scripts $JUPYTER_HOME/scripts +COPY custom $JUPYTER_HOME/custom +COPY jupyter_notebook_config.py $JUPYTER_HOME + +# copy workspace_cromwell.py script and make it runnable by all users +RUN curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ + && chmod +x /usr/local/bin/workspace_cromwell.py + +RUN chown -R $USER:users $JUPYTER_HOME \ +# Disable nb_conda for now. Consider re-enable in the future +# && jupyter nbextension disable nb_conda --py --sys-prefix \ + && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ +# You can get kernel directory by running `jupyter kernelspec list` + && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel /opt/conda/share/jupyter/kernels + +USER $USER +EXPOSE $JUPYTER_PORT +WORKDIR $HOME + +# Note: this entrypoint is provided for running Jupyter independently of Leonardo. +# When Leonardo deploys this image onto a cluster, the entrypoint is overwritten to enable +# additional setup inside the container before execution. Jupyter execution occurs when the +# init-actions.sh script uses 'docker exec' to call run-jupyter.sh. +ENTRYPOINT ["/opt/conda/bin/jupyter", "notebook"] diff --git a/terra-jupyter-dev-base/build_docker.sh b/terra-jupyter-dev-base/build_docker.sh new file mode 100755 index 00000000..2feeaebd --- /dev/null +++ b/terra-jupyter-dev-base/build_docker.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t terra-jupyter-dev-base:0.0.1 -f Dockerfile ../terra-jupyter-base/ From 042759b4e8f9fd9de4d909e78f745050583b6729 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Thu, 4 Aug 2022 17:57:01 -0400 Subject: [PATCH 02/26] First working(?) version --- terra-jupyter-dev-base/Dockerfile | 111 +++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/terra-jupyter-dev-base/Dockerfile b/terra-jupyter-dev-base/Dockerfile index 2050f5d2..cd34eb31 100644 --- a/terra-jupyter-dev-base/Dockerfile +++ b/terra-jupyter-dev-base/Dockerfile @@ -19,39 +19,41 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ # gnupg requirement dirmngr \ gnupg \ - # curl requirement +# # curl requirement curl \ ca-certificates \ # useful utilities for debugging within the docker nano \ procps \ - lsb-release \ - # python requirements - checkinstall \ - build-essential \ - zlib1g-dev \ - # pip requirements - libssl-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - llvm \ - libncurses5-dev \ - libncursesw5-dev \ - tk-dev \ - libffi-dev \ - liblzma-dev \ - python-openssl \ - libexempi3 \ - libv8-3.14-dev \ +# lsb-release \ +# # python requirements +# checkinstall \ +# build-essential \ +# zlib1g-dev \ +# # pip requirements +# libssl-dev \ +# libbz2-dev \ +# libreadline-dev \ +# libsqlite3-dev \ +# llvm \ +# libncurses5-dev \ +# libncursesw5-dev \ +# tk-dev \ +# libffi-dev \ +# liblzma-dev \ +# python-openssl \ +# libexempi3 \ +# libv8-3.14-dev \ + # extras \ + wget \ + bzip2 \ # install script requirements - sudo \ locales \ # for ssh-agent and ssh-add keychain \ - # openjdk 11 - default-jre \ - default-jdk \ +## # openjdk 11 +## default-jre \ +## default-jdk \ # git git \ # Uncomment en_US.UTF-8 for inclusion in generation @@ -86,16 +88,58 @@ ENV JUPYTER_HOME /etc/jupyter # install miniconda to /opt/conda ENV CONDA_AUTO_UPDATE_CONDA=false -ENV PATH="${PATH}:/opt/conda/bin:${HOME}/.local/bin:${HOME}/packages/bin" +ENV CONDA_DIR /opt/conda +ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" RUN curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ - && $HOME/miniconda.sh -b -p /opt/conda \ + && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh +# slim install of things that have a gcc dependency +RUN apt-get update && apt-get install -y --no-install-recommends \ + checkinstall \ + build-essential \ + zlib1g-dev \ + # pip requirements + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + libexempi3 \ + libv8-3.14-dev \ + && rm -rf /var/lib/apt/lists/* \ + && pip3 install firecloud \ + && pip3 install terra-notebook-utils \ + && apt-get purge -y --auto-remove \ + checkinstall \ + build-essential \ + zlib1g-dev \ + # pip requirements + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + libexempi3 \ + libv8-3.14-dev + RUN pip3 -V \ # For gcloud alpha storage support. && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ - && git clone --branch sf_missing_requirement --single-branch \ + && git clone --branch cromshell_2.0 --single-branch \ https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ # tmp hack min-5 # I'm not installing jupyterlab and I can't update init-actions.sh to not access it @@ -104,7 +148,10 @@ RUN pip3 -V \ # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this && pip3 install notebook \ - && pip3 install ipykernel \ + && pip3 install jupyter \ +# && pip3 install jupyterlab \ + && pip3 install cookiecutter \ + && pip3 install tornado \ && pip3 install python-datauri \ && pip3 install jupyter_contrib_nbextensions \ && pip3 install jupyter_nbextensions_configurator \ @@ -113,8 +160,8 @@ RUN pip3 -V \ && pip3 install "nbconvert>=6.4.5" \ # for jupyter_delocalize.py and jupyter_notebook_config.py && pip3 install requests \ - && pip3 install firecloud \ - && pip3 install terra-notebook-utils \ +# && pip3 install firecloud \ +# && pip3 install terra-notebook-utils \ && pip3 install crcmod \ # install Cromshell 2.0, using repo cloned above && pip install $HOME/cromshell @@ -144,7 +191,7 @@ RUN chown -R $USER:users $JUPYTER_HOME \ # && jupyter nbextension disable nb_conda --py --sys-prefix \ && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ # You can get kernel directory by running `jupyter kernelspec list` - && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel /opt/conda/share/jupyter/kernels + && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels USER $USER EXPOSE $JUPYTER_PORT @@ -154,4 +201,4 @@ WORKDIR $HOME # When Leonardo deploys this image onto a cluster, the entrypoint is overwritten to enable # additional setup inside the container before execution. Jupyter execution occurs when the # init-actions.sh script uses 'docker exec' to call run-jupyter.sh. -ENTRYPOINT ["/opt/conda/bin/jupyter", "notebook"] +ENTRYPOINT ["jupyter", "notebook"] From 186a8b7225a04a2a704f26bbb939fdf4bd3e03b8 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 5 Aug 2022 09:53:02 -0400 Subject: [PATCH 03/26] Attempted size minimization: 1.77GB --- terra-jupyter-dev-base/Dockerfile | 137 +++++++++++------------------- 1 file changed, 48 insertions(+), 89 deletions(-) diff --git a/terra-jupyter-dev-base/Dockerfile b/terra-jupyter-dev-base/Dockerfile index cd34eb31..0c135062 100644 --- a/terra-jupyter-dev-base/Dockerfile +++ b/terra-jupyter-dev-base/Dockerfile @@ -2,48 +2,49 @@ FROM ubuntu:20.04 USER root -####################### -# Prerequisites -####################### -ENV DEBIAN_FRONTEND noninteractive +# The welder uid is consistent with the Welder docker definition here: +# https://github.com/DataBiosphere/welder/blob/master/project/Settings.scala +# Adding welder-user to the Jupyter container isn't strictly required, but it makes welder-added +# files display nicer when viewed in a terminal. +ENV DEBIAN_FRONTEND=noninteractive \ + LC_ALL=en_US.UTF-8 \ + USER=jupyter \ + WELDER_USER=welder-user \ + WELDER_UID=1001 \ + # ensure this matches c.NotebookApp.port in jupyter_notebook_config.py + JUPYTER_PORT=8000 \ + JUPYTER_HOME=/etc/jupyter \ + CONDA_AUTO_UPDATE_CONDA=false \ + CONDA_DIR=/opt/conda +ENV HOME=/home/$USER + # When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. + # Append '/home/jupyter/.local/bin' to PATH + # pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user +ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" -RUN apt-get update && apt-get install -yq --no-install-recommends sudo \ +# Users +RUN useradd -m -s /bin/bash $USER \ + && usermod -g users $USER \ + && useradd -m -s /bin/bash -N -u $WELDER_UID $WELDER_USER \ +# Prerequisites + && apt-get update && apt-get install -yq --no-install-recommends \ + sudo \ && sudo -i \ echo "deb http://security.ubuntu.com/ubuntu/ bionic main" >> /etc/apt/sources.list \ sudo apt update libexempi3 \ && sudo -i \ echo "deb http://us.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list \ - sudo apt update libv8-3.14-dev - -RUN apt-get update && apt-get install -yq --no-install-recommends \ + sudo apt update libv8-3.14-dev \ + && apt-get update && apt-get install -yq --no-install-recommends \ # gnupg requirement dirmngr \ gnupg \ -# # curl requirement + # curl requirement curl \ ca-certificates \ # useful utilities for debugging within the docker nano \ procps \ -# lsb-release \ -# # python requirements -# checkinstall \ -# build-essential \ -# zlib1g-dev \ -# # pip requirements -# libssl-dev \ -# libbz2-dev \ -# libreadline-dev \ -# libsqlite3-dev \ -# llvm \ -# libncurses5-dev \ -# libncursesw5-dev \ -# tk-dev \ -# libffi-dev \ -# liblzma-dev \ -# python-openssl \ -# libexempi3 \ -# libv8-3.14-dev \ # extras \ wget \ bzip2 \ @@ -51,52 +52,21 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ locales \ # for ssh-agent and ssh-add keychain \ -## # openjdk 11 -## default-jre \ -## default-jdk \ # git git \ -# Uncomment en_US.UTF-8 for inclusion in generation + # Uncomment en_US.UTF-8 for inclusion in generation && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ # Generate locale && locale-gen \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ENV LC_ALL en_US.UTF-8 - -####################### -# Python / Jupyter -####################### - -ENV USER jupyter -RUN useradd -m -s /bin/bash $USER \ - && usermod -g users $USER -ENV HOME /home/$USER - -# The welder uid is consistent with the Welder docker definition here: -# https://github.com/DataBiosphere/welder/blob/master/project/Settings.scala -# Adding welder-user to the Jupyter container isn't strictly required, but it makes welder-added -# files display nicer when viewed in a terminal. -ENV WELDER_USER welder-user -ENV WELDER_UID 1001 -RUN useradd -m -s /bin/bash -N -u $WELDER_UID $WELDER_USER - -# ensure this matches c.NotebookApp.port in jupyter_notebook_config.py -ENV JUPYTER_PORT 8000 -ENV JUPYTER_HOME /etc/jupyter - -# install miniconda to /opt/conda -ENV CONDA_AUTO_UPDATE_CONDA=false -ENV CONDA_DIR /opt/conda -ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" -RUN curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ + && rm -rf /var/lib/apt/lists/* \ +# install miniconda to $CONDA_DIR + && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ - && rm $HOME/miniconda.sh - -# slim install of things that have a gcc dependency -RUN apt-get update && apt-get install -y --no-install-recommends \ + && rm $HOME/miniconda.sh \ +# slim install of python packages that have a gcc dependency (cleanup included) + && apt-get update && apt-get install -y --no-install-recommends \ checkinstall \ build-essential \ zlib1g-dev \ @@ -134,9 +104,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ liblzma-dev \ python-openssl \ libexempi3 \ - libv8-3.14-dev - -RUN pip3 -V \ + libv8-3.14-dev \ +# Install jupyter and some necessary python packages + && pip3 -V \ # For gcloud alpha storage support. && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ && git clone --branch cromshell_2.0 --single-branch \ @@ -160,39 +130,28 @@ RUN pip3 -V \ && pip3 install "nbconvert>=6.4.5" \ # for jupyter_delocalize.py and jupyter_notebook_config.py && pip3 install requests \ -# && pip3 install firecloud \ -# && pip3 install terra-notebook-utils \ && pip3 install crcmod \ # install Cromshell 2.0, using repo cloned above - && pip install $HOME/cromshell - -# make pip install to a user directory, instead of a system directory which requires root. -# this is useful so `pip install` commands can be run in the context of a notebook. -ENV PIP_USER=true -# When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. -# Append '/home/jupyter/.local/bin' to PATH -# pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user -#ENV PATH="${PATH}:${HOME}/.local/bin:${HOME}/packages/bin" + && pip install $HOME/cromshell \ +# copy workspace_cromwell.py script and make it runnable by all users + && curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ + && chmod +x /usr/local/bin/workspace_cromwell.py -####################### # Utilities -####################### - COPY scripts $JUPYTER_HOME/scripts COPY custom $JUPYTER_HOME/custom COPY jupyter_notebook_config.py $JUPYTER_HOME -# copy workspace_cromwell.py script and make it runnable by all users -RUN curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ - && chmod +x /usr/local/bin/workspace_cromwell.py - RUN chown -R $USER:users $JUPYTER_HOME \ -# Disable nb_conda for now. Consider re-enable in the future + # Disable nb_conda for now. Consider re-enable in the future # && jupyter nbextension disable nb_conda --py --sys-prefix \ && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ -# You can get kernel directory by running `jupyter kernelspec list` + # You can get kernel directory by running `jupyter kernelspec list` && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels +# make pip install to a user directory, instead of a system directory which requires root. +# this is useful so `pip install` commands can be run in the context of a notebook. +ENV PIP_USER=true USER $USER EXPOSE $JUPYTER_PORT WORKDIR $HOME From f9f9d75e15745e416ea718f19c098f69b5ba32cd Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 5 Aug 2022 17:15:09 -0400 Subject: [PATCH 04/26] Permissions for conda installs --- terra-jupyter-dev-base/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terra-jupyter-dev-base/Dockerfile b/terra-jupyter-dev-base/Dockerfile index 0c135062..0269e95c 100644 --- a/terra-jupyter-dev-base/Dockerfile +++ b/terra-jupyter-dev-base/Dockerfile @@ -143,7 +143,9 @@ COPY custom $JUPYTER_HOME/custom COPY jupyter_notebook_config.py $JUPYTER_HOME RUN chown -R $USER:users $JUPYTER_HOME \ - # Disable nb_conda for now. Consider re-enable in the future + && chown -R $USER:users $HOME/.conda \ + && chown -R $USER:users $CONDA_DIR \ +# Disable nb_conda for now. Consider re-enable in the future # && jupyter nbextension disable nb_conda --py --sys-prefix \ && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ # You can get kernel directory by running `jupyter kernelspec list` From c5891655b6821b00e70e77e5b28f01c287bdfd71 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 5 Aug 2022 17:31:52 -0400 Subject: [PATCH 05/26] GPU-enabled base image: 2.63 GB --- terra-jupyter-gpu-dev-base/Dockerfile | 165 +++++++++++++++++++++ terra-jupyter-gpu-dev-base/build_docker.sh | 3 + 2 files changed, 168 insertions(+) create mode 100644 terra-jupyter-gpu-dev-base/Dockerfile create mode 100755 terra-jupyter-gpu-dev-base/build_docker.sh diff --git a/terra-jupyter-gpu-dev-base/Dockerfile b/terra-jupyter-gpu-dev-base/Dockerfile new file mode 100644 index 00000000..916b3ca2 --- /dev/null +++ b/terra-jupyter-gpu-dev-base/Dockerfile @@ -0,0 +1,165 @@ +FROM nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04 + +USER root + +# The welder uid is consistent with the Welder docker definition here: +# https://github.com/DataBiosphere/welder/blob/master/project/Settings.scala +# Adding welder-user to the Jupyter container isn't strictly required, but it makes welder-added +# files display nicer when viewed in a terminal. +ENV DEBIAN_FRONTEND=noninteractive \ + LC_ALL=en_US.UTF-8 \ + USER=jupyter \ + WELDER_USER=welder-user \ + WELDER_UID=1001 \ + # ensure this matches c.NotebookApp.port in jupyter_notebook_config.py + JUPYTER_PORT=8000 \ + JUPYTER_HOME=/etc/jupyter \ + CONDA_AUTO_UPDATE_CONDA=false \ + CONDA_DIR=/opt/conda +ENV HOME=/home/$USER + # When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. + # Append '/home/jupyter/.local/bin' to PATH + # pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user +ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" + +# Users +RUN useradd -m -s /bin/bash $USER \ + && usermod -g users $USER \ + && useradd -m -s /bin/bash -N -u $WELDER_UID $WELDER_USER \ +# Prerequisites + && apt-get update && apt-get install -yq --no-install-recommends \ + sudo \ + && sudo -i \ + echo "deb http://security.ubuntu.com/ubuntu/ bionic main" >> /etc/apt/sources.list \ + sudo apt update libexempi3 \ + && sudo -i \ + echo "deb http://us.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list \ + sudo apt update libv8-3.14-dev \ + && apt-get update && apt-get install -yq --no-install-recommends \ + # gnupg requirement + dirmngr \ + gnupg \ + # curl requirement + curl \ + ca-certificates \ + # useful utilities for debugging within the docker + nano \ + procps \ + # extras \ + wget \ + bzip2 \ + # install script requirements + locales \ + # for ssh-agent and ssh-add + keychain \ + # git + git \ + # Uncomment en_US.UTF-8 for inclusion in generation + && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ + # Generate locale + && locale-gen \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ +# install miniconda to $CONDA_DIR + && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ + && chmod +x $HOME/miniconda.sh \ + && $HOME/miniconda.sh -b -p $CONDA_DIR \ + && rm $HOME/miniconda.sh \ +# slim install of python packages that have a gcc dependency (cleanup included) + && apt-get update && apt-get install -y --no-install-recommends \ + checkinstall \ + build-essential \ + zlib1g-dev \ + # pip requirements + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + libexempi3 \ + libv8-3.14-dev \ + && rm -rf /var/lib/apt/lists/* \ + && pip3 install firecloud \ + && pip3 install terra-notebook-utils \ + && apt-get purge -y --auto-remove \ + checkinstall \ + build-essential \ + zlib1g-dev \ + # pip requirements + libssl-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + libexempi3 \ + libv8-3.14-dev \ +# Install jupyter and some necessary python packages + && pip3 -V \ + # For gcloud alpha storage support. + && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ + && git clone --branch cromshell_2.0 --single-branch \ + https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ + # tmp hack min-5 + # I'm not installing jupyterlab and I can't update init-actions.sh to not access it + && mkdir -p /usr/local/share/jupyter/lab \ + # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. + # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) + # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this + && pip3 install notebook \ + && pip3 install jupyter \ +# && pip3 install jupyterlab \ + && pip3 install cookiecutter \ + && pip3 install tornado \ + && pip3 install python-datauri \ + && pip3 install jupyter_contrib_nbextensions \ + && pip3 install jupyter_nbextensions_configurator \ + && pip3 install markupsafe==2.0.1 \ + # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624 + && pip3 install "nbconvert>=6.4.5" \ + # for jupyter_delocalize.py and jupyter_notebook_config.py + && pip3 install requests \ + && pip3 install crcmod \ + # install Cromshell 2.0, using repo cloned above + && pip install $HOME/cromshell \ +# copy workspace_cromwell.py script and make it runnable by all users + && curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ + && chmod +x /usr/local/bin/workspace_cromwell.py + +# Utilities +COPY scripts $JUPYTER_HOME/scripts +COPY custom $JUPYTER_HOME/custom +COPY jupyter_notebook_config.py $JUPYTER_HOME + +RUN chown -R $USER:users $JUPYTER_HOME \ + && chown -R $USER:users $HOME/.conda \ + && chown -R $USER:users $CONDA_DIR \ +# Disable nb_conda for now. Consider re-enable in the future +# && jupyter nbextension disable nb_conda --py --sys-prefix \ + && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ + # You can get kernel directory by running `jupyter kernelspec list` + && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels + +# make pip install to a user directory, instead of a system directory which requires root. +# this is useful so `pip install` commands can be run in the context of a notebook. +ENV PIP_USER=true +USER $USER +EXPOSE $JUPYTER_PORT +WORKDIR $HOME + +# Note: this entrypoint is provided for running Jupyter independently of Leonardo. +# When Leonardo deploys this image onto a cluster, the entrypoint is overwritten to enable +# additional setup inside the container before execution. Jupyter execution occurs when the +# init-actions.sh script uses 'docker exec' to call run-jupyter.sh. +ENTRYPOINT ["jupyter", "notebook"] diff --git a/terra-jupyter-gpu-dev-base/build_docker.sh b/terra-jupyter-gpu-dev-base/build_docker.sh new file mode 100755 index 00000000..b4d31db3 --- /dev/null +++ b/terra-jupyter-gpu-dev-base/build_docker.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t terra-jupyter-gpu-dev-base:0.0.1 -f Dockerfile ../terra-jupyter-base/ From 7cbc9d2fb428268adebaf9083b6d0a574466466c Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Tue, 30 Aug 2022 16:25:02 -0400 Subject: [PATCH 06/26] GPU image uses same Dockerfile; requirements files --- terra-jupyter-dev-base/build_docker.sh | 3 - terra-jupyter-gpu-dev-base/Dockerfile | 165 ------------------ terra-jupyter-gpu-dev-base/build_docker.sh | 3 - .../Dockerfile | 104 ++++------- terra-jupyter-minimal-base/build_docker.sh | 17 ++ terra-jupyter-minimal-base/gcc_pkgs.txt | 16 ++ terra-jupyter-minimal-base/requirements.txt | 11 ++ .../requirements_gcc.txt | 3 + .../build_docker.sh | 17 ++ 9 files changed, 100 insertions(+), 239 deletions(-) delete mode 100755 terra-jupyter-dev-base/build_docker.sh delete mode 100644 terra-jupyter-gpu-dev-base/Dockerfile delete mode 100755 terra-jupyter-gpu-dev-base/build_docker.sh rename {terra-jupyter-dev-base => terra-jupyter-minimal-base}/Dockerfile (66%) create mode 100755 terra-jupyter-minimal-base/build_docker.sh create mode 100644 terra-jupyter-minimal-base/gcc_pkgs.txt create mode 100644 terra-jupyter-minimal-base/requirements.txt create mode 100644 terra-jupyter-minimal-base/requirements_gcc.txt create mode 100755 terra-jupyter-minimal-gpu-base/build_docker.sh diff --git a/terra-jupyter-dev-base/build_docker.sh b/terra-jupyter-dev-base/build_docker.sh deleted file mode 100755 index 2feeaebd..00000000 --- a/terra-jupyter-dev-base/build_docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t terra-jupyter-dev-base:0.0.1 -f Dockerfile ../terra-jupyter-base/ diff --git a/terra-jupyter-gpu-dev-base/Dockerfile b/terra-jupyter-gpu-dev-base/Dockerfile deleted file mode 100644 index 916b3ca2..00000000 --- a/terra-jupyter-gpu-dev-base/Dockerfile +++ /dev/null @@ -1,165 +0,0 @@ -FROM nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04 - -USER root - -# The welder uid is consistent with the Welder docker definition here: -# https://github.com/DataBiosphere/welder/blob/master/project/Settings.scala -# Adding welder-user to the Jupyter container isn't strictly required, but it makes welder-added -# files display nicer when viewed in a terminal. -ENV DEBIAN_FRONTEND=noninteractive \ - LC_ALL=en_US.UTF-8 \ - USER=jupyter \ - WELDER_USER=welder-user \ - WELDER_UID=1001 \ - # ensure this matches c.NotebookApp.port in jupyter_notebook_config.py - JUPYTER_PORT=8000 \ - JUPYTER_HOME=/etc/jupyter \ - CONDA_AUTO_UPDATE_CONDA=false \ - CONDA_DIR=/opt/conda -ENV HOME=/home/$USER - # When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. - # Append '/home/jupyter/.local/bin' to PATH - # pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user -ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" - -# Users -RUN useradd -m -s /bin/bash $USER \ - && usermod -g users $USER \ - && useradd -m -s /bin/bash -N -u $WELDER_UID $WELDER_USER \ -# Prerequisites - && apt-get update && apt-get install -yq --no-install-recommends \ - sudo \ - && sudo -i \ - echo "deb http://security.ubuntu.com/ubuntu/ bionic main" >> /etc/apt/sources.list \ - sudo apt update libexempi3 \ - && sudo -i \ - echo "deb http://us.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list \ - sudo apt update libv8-3.14-dev \ - && apt-get update && apt-get install -yq --no-install-recommends \ - # gnupg requirement - dirmngr \ - gnupg \ - # curl requirement - curl \ - ca-certificates \ - # useful utilities for debugging within the docker - nano \ - procps \ - # extras \ - wget \ - bzip2 \ - # install script requirements - locales \ - # for ssh-agent and ssh-add - keychain \ - # git - git \ - # Uncomment en_US.UTF-8 for inclusion in generation - && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ - # Generate locale - && locale-gen \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ -# install miniconda to $CONDA_DIR - && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ - && chmod +x $HOME/miniconda.sh \ - && $HOME/miniconda.sh -b -p $CONDA_DIR \ - && rm $HOME/miniconda.sh \ -# slim install of python packages that have a gcc dependency (cleanup included) - && apt-get update && apt-get install -y --no-install-recommends \ - checkinstall \ - build-essential \ - zlib1g-dev \ - # pip requirements - libssl-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - llvm \ - libncurses5-dev \ - libncursesw5-dev \ - tk-dev \ - libffi-dev \ - liblzma-dev \ - python-openssl \ - libexempi3 \ - libv8-3.14-dev \ - && rm -rf /var/lib/apt/lists/* \ - && pip3 install firecloud \ - && pip3 install terra-notebook-utils \ - && apt-get purge -y --auto-remove \ - checkinstall \ - build-essential \ - zlib1g-dev \ - # pip requirements - libssl-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - llvm \ - libncurses5-dev \ - libncursesw5-dev \ - tk-dev \ - libffi-dev \ - liblzma-dev \ - python-openssl \ - libexempi3 \ - libv8-3.14-dev \ -# Install jupyter and some necessary python packages - && pip3 -V \ - # For gcloud alpha storage support. - && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ - && git clone --branch cromshell_2.0 --single-branch \ - https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ - # tmp hack min-5 - # I'm not installing jupyterlab and I can't update init-actions.sh to not access it - && mkdir -p /usr/local/share/jupyter/lab \ - # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. - # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) - # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this - && pip3 install notebook \ - && pip3 install jupyter \ -# && pip3 install jupyterlab \ - && pip3 install cookiecutter \ - && pip3 install tornado \ - && pip3 install python-datauri \ - && pip3 install jupyter_contrib_nbextensions \ - && pip3 install jupyter_nbextensions_configurator \ - && pip3 install markupsafe==2.0.1 \ - # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624 - && pip3 install "nbconvert>=6.4.5" \ - # for jupyter_delocalize.py and jupyter_notebook_config.py - && pip3 install requests \ - && pip3 install crcmod \ - # install Cromshell 2.0, using repo cloned above - && pip install $HOME/cromshell \ -# copy workspace_cromwell.py script and make it runnable by all users - && curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ - && chmod +x /usr/local/bin/workspace_cromwell.py - -# Utilities -COPY scripts $JUPYTER_HOME/scripts -COPY custom $JUPYTER_HOME/custom -COPY jupyter_notebook_config.py $JUPYTER_HOME - -RUN chown -R $USER:users $JUPYTER_HOME \ - && chown -R $USER:users $HOME/.conda \ - && chown -R $USER:users $CONDA_DIR \ -# Disable nb_conda for now. Consider re-enable in the future -# && jupyter nbextension disable nb_conda --py --sys-prefix \ - && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ - # You can get kernel directory by running `jupyter kernelspec list` - && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels - -# make pip install to a user directory, instead of a system directory which requires root. -# this is useful so `pip install` commands can be run in the context of a notebook. -ENV PIP_USER=true -USER $USER -EXPOSE $JUPYTER_PORT -WORKDIR $HOME - -# Note: this entrypoint is provided for running Jupyter independently of Leonardo. -# When Leonardo deploys this image onto a cluster, the entrypoint is overwritten to enable -# additional setup inside the container before execution. Jupyter execution occurs when the -# init-actions.sh script uses 'docker exec' to call run-jupyter.sh. -ENTRYPOINT ["jupyter", "notebook"] diff --git a/terra-jupyter-gpu-dev-base/build_docker.sh b/terra-jupyter-gpu-dev-base/build_docker.sh deleted file mode 100755 index b4d31db3..00000000 --- a/terra-jupyter-gpu-dev-base/build_docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t terra-jupyter-gpu-dev-base:0.0.1 -f Dockerfile ../terra-jupyter-base/ diff --git a/terra-jupyter-dev-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile similarity index 66% rename from terra-jupyter-dev-base/Dockerfile rename to terra-jupyter-minimal-base/Dockerfile index 0269e95c..ec277a56 100644 --- a/terra-jupyter-dev-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -1,4 +1,6 @@ -FROM ubuntu:20.04 +ARG BASE_IMAGE="ubuntu:20.04" + +FROM ${BASE_IMAGE} USER root @@ -8,6 +10,7 @@ USER root # files display nicer when viewed in a terminal. ENV DEBIAN_FRONTEND=noninteractive \ LC_ALL=en_US.UTF-8 \ + GOOGLE_CLOUD_CLI_VERSION="397.0.0" \ USER=jupyter \ WELDER_USER=welder-user \ WELDER_UID=1001 \ @@ -20,7 +23,11 @@ ENV HOME=/home/$USER # When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. # Append '/home/jupyter/.local/bin' to PATH # pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user -ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin" +ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin:${HOME}/google-cloud-sdk/bin:" + +COPY requirements.txt /opt/ +COPY requirements_gcc.txt /opt/ +COPY gcc_pkgs.txt /opt/ # Users RUN useradd -m -s /bin/bash $USER \ @@ -60,82 +67,44 @@ RUN useradd -m -s /bin/bash $USER \ && locale-gen \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ -# install miniconda to $CONDA_DIR +# Install miniconda to $CONDA_DIR && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ -# slim install of python packages that have a gcc dependency (cleanup included) - && apt-get update && apt-get install -y --no-install-recommends \ - checkinstall \ - build-essential \ - zlib1g-dev \ - # pip requirements - libssl-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - llvm \ - libncurses5-dev \ - libncursesw5-dev \ - tk-dev \ - libffi-dev \ - liblzma-dev \ - python-openssl \ - libexempi3 \ - libv8-3.14-dev \ - && rm -rf /var/lib/apt/lists/* \ - && pip3 install firecloud \ - && pip3 install terra-notebook-utils \ +# Install gsutil with compiled crcmod + && curl -so $HOME/google-cloud-cli.tar.gz \ + https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-${GOOGLE_CLOUD_CLI_VERSION}-linux-x86_64.tar.gz \ + && tar -xzf $HOME/google-cloud-cli.tar.gz -C $HOME \ + && .$HOME/google-cloud-sdk/install.sh \ + && rm $HOME/google-cloud-cli.tar.gz \ + && rm -rf ~/.cache/pip \ +# Slim install of python packages that have a gcc dependency (cleanup included) + && apt-get update && apt-get install -yq --no-install-recommends \ + $(cat /opt/gcc_pkgs.txt) \ + && pip3 install -r /opt/requirements_gcc.txt \ && apt-get purge -y --auto-remove \ - checkinstall \ - build-essential \ - zlib1g-dev \ - # pip requirements - libssl-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - llvm \ - libncurses5-dev \ - libncursesw5-dev \ - tk-dev \ - libffi-dev \ - liblzma-dev \ - python-openssl \ - libexempi3 \ - libv8-3.14-dev \ + $(cat /opt/gcc_pkgs.txt) \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ # Install jupyter and some necessary python packages - && pip3 -V \ - # For gcloud alpha storage support. - && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ - && git clone --branch cromshell_2.0 --single-branch \ - https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ # tmp hack min-5 # I'm not installing jupyterlab and I can't update init-actions.sh to not access it && mkdir -p /usr/local/share/jupyter/lab \ # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this - && pip3 install notebook \ - && pip3 install jupyter \ -# && pip3 install jupyterlab \ - && pip3 install cookiecutter \ - && pip3 install tornado \ - && pip3 install python-datauri \ - && pip3 install jupyter_contrib_nbextensions \ - && pip3 install jupyter_nbextensions_configurator \ - && pip3 install markupsafe==2.0.1 \ - # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624 - && pip3 install "nbconvert>=6.4.5" \ - # for jupyter_delocalize.py and jupyter_notebook_config.py - && pip3 install requests \ - && pip3 install crcmod \ - # install Cromshell 2.0, using repo cloned above - && pip install $HOME/cromshell \ -# copy workspace_cromwell.py script and make it runnable by all users - && curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ - && chmod +x /usr/local/bin/workspace_cromwell.py + && pip3 install -r /opt/requirements.txt \ +# Copy workspace_cromwell.py script and make it runnable by all users + && curl -o /usr/local/bin/workspace_cromwell.py \ + https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ + && chmod +x /usr/local/bin/workspace_cromwell.py \ +# Cromshell 2.0 + && git clone --branch cromshell_2.0 --single-branch \ + https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ + && pip3 install $HOME/cromshell \ + && rm -rf ~/.cache/pip \ + && conda clean -ya # Utilities COPY scripts $JUPYTER_HOME/scripts @@ -145,8 +114,7 @@ COPY jupyter_notebook_config.py $JUPYTER_HOME RUN chown -R $USER:users $JUPYTER_HOME \ && chown -R $USER:users $HOME/.conda \ && chown -R $USER:users $CONDA_DIR \ -# Disable nb_conda for now. Consider re-enable in the future -# && jupyter nbextension disable nb_conda --py --sys-prefix \ + && chown -R $USER:users $HOME/.config/gcloud \ && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ # You can get kernel directory by running `jupyter kernelspec list` && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels diff --git a/terra-jupyter-minimal-base/build_docker.sh b/terra-jupyter-minimal-base/build_docker.sh new file mode 100755 index 00000000..e3519f30 --- /dev/null +++ b/terra-jupyter-minimal-base/build_docker.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# this copying stuff is here because there are requirements here and in ../terra-jupyter-base/ +# but the build context cannot be both +cp -r ../terra-jupyter-base/custom custom +cp -r ../terra-jupyter-base/scripts scripts +cp -r ../terra-jupyter-base/jupyter_notebook_config.py jupyter_notebook_config.py + +docker build -t terra-jupyter-minimal-base:0.0.1 . + +rm -r custom +rm -r scripts +rm jupyter_notebook_config.py + +# if requirements.txt, requirements_gcc.txt, and gcc_pkgs.txt get moved to the ../terra-jupyter-base/ folder, +# then all of the above could be replaced by this: +#docker build -t terra-jupyter-minimal-base:0.0.1 -f Dockerfile ../terra-jupyter-base/ diff --git a/terra-jupyter-minimal-base/gcc_pkgs.txt b/terra-jupyter-minimal-base/gcc_pkgs.txt new file mode 100644 index 00000000..74d41996 --- /dev/null +++ b/terra-jupyter-minimal-base/gcc_pkgs.txt @@ -0,0 +1,16 @@ +checkinstall +build-essential +zlib1g-dev +libssl-dev +libbz2-dev +libreadline-dev +libsqlite3-dev +llvm +libncurses5-dev +libncursesw5-dev +tk-dev +libffi-dev +liblzma-dev +python-openssl +libexempi3 +libv8-3.14-dev \ No newline at end of file diff --git a/terra-jupyter-minimal-base/requirements.txt b/terra-jupyter-minimal-base/requirements.txt new file mode 100644 index 00000000..f2e668d0 --- /dev/null +++ b/terra-jupyter-minimal-base/requirements.txt @@ -0,0 +1,11 @@ +notebook==6.4.12 +jupyter==1.0.0 +cookiecutter==2.1.1 +tornado==6.2 +python-datauri==1.1.0 +jupyter_contrib_nbextensions==0.5.1 +jupyter_nbextensions_configurator==0.5.0 +markupsafe==2.0.1 +nbconvert>=6.4.5 +requests==2.27.1 +crcmod==1.7 \ No newline at end of file diff --git a/terra-jupyter-minimal-base/requirements_gcc.txt b/terra-jupyter-minimal-base/requirements_gcc.txt new file mode 100644 index 00000000..033fc7bb --- /dev/null +++ b/terra-jupyter-minimal-base/requirements_gcc.txt @@ -0,0 +1,3 @@ +terra-notebook-utils==0.10.0 +firecloud==0.16.32.post1 +crcmod==1.7 \ No newline at end of file diff --git a/terra-jupyter-minimal-gpu-base/build_docker.sh b/terra-jupyter-minimal-gpu-base/build_docker.sh new file mode 100755 index 00000000..71056d5a --- /dev/null +++ b/terra-jupyter-minimal-gpu-base/build_docker.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +base="nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04" + +cp -r ../terra-jupyter-base/custom ../terra-jupyter-minimal-base/custom +cp -r ../terra-jupyter-base/scripts ../terra-jupyter-minimal-base/scripts +cp -r ../terra-jupyter-base/jupyter_notebook_config.py ../terra-jupyter-minimal-base/jupyter_notebook_config.py + +docker build \ + -t terra-jupyter-minimal-gpu-base:0.0.1 \ + -f ../terra-jupyter-minimal-base/Dockerfile \ + --build-arg BASE_IMAGE=${base} \ + ../terra-jupyter-minimal-base/ + +rm -r ../terra-jupyter-minimal-base/custom +rm -r ../terra-jupyter-minimal-base/scripts +rm ../terra-jupyter-minimal-base/jupyter_notebook_config.py From 27b354106cf8152ccf1a92a762628bdd0d8a82de Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Tue, 30 Aug 2022 16:25:42 -0400 Subject: [PATCH 07/26] Add documentation --- README.md | 6 +++ terra-jupyter-minimal-base/CHANGELOG.md | 11 ++++++ terra-jupyter-minimal-base/README.md | 30 +++++++++++++++ terra-jupyter-minimal-gpu-base/CHANGELOG.md | 13 +++++++ terra-jupyter-minimal-gpu-base/README.md | 42 +++++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 terra-jupyter-minimal-base/CHANGELOG.md create mode 100644 terra-jupyter-minimal-base/README.md create mode 100644 terra-jupyter-minimal-gpu-base/CHANGELOG.md create mode 100644 terra-jupyter-minimal-gpu-base/README.md diff --git a/README.md b/README.md index a75a7af1..8ee610c0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ Make sure to go through the [contributing guide](https://github.com/DataBiospher [terra-jupyter-bioconductor](terra-jupyter-bioconductor/README.md) +## (Small base images for developers) +[terra-jupyter-minimal-base](terra-jupyter-minimal-base/README.md) + +[terra-jupyter-minimal-gpu-base](terra-jupyter-minimal-gpu-base/README.md) + # How to create your own Custom image to use with notebooks on Terra Custom docker images need to use a Terra base image (see above) in order to work with the service that runs notebooks on Terra. * You can use any of the base images above @@ -29,6 +34,7 @@ Custom docker images need to use a Terra base image (see above) in order to work * Since 6/28/2021, we introduced a few changes that might impact building custom images - Home directory of new images will be `/home/jupyter`. This means if your dockerfile is referencing `/home/jupyter-user` directory, you need to update it to $HOME (recommended) or `/home/jupyter`. - Creating VMs with custom images will take much longer than terra supported images because `docker pull` will take a few min. If the custom image ends up being too large, VM creation may time out. New base images are much larger in size than previous versions. + - Consider using the "minimal" base images # Development ## Using git secrets diff --git a/terra-jupyter-minimal-base/CHANGELOG.md b/terra-jupyter-minimal-base/CHANGELOG.md new file mode 100644 index 00000000..7b3cf848 --- /dev/null +++ b/terra-jupyter-minimal-base/CHANGELOG.md @@ -0,0 +1,11 @@ +## 0.0.1 - 08/30/2022 + +- Extends Ubuntu 20.04 base image +- Add google-cloud-cli +- Add Python 3.7 +- Add Miniconda +- Add Jupyter +- Add Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + +Image URL: `us.gcr.io/broad-dsde-methods/terra-jupyter-minimal-base:0.0.1` diff --git a/terra-jupyter-minimal-base/README.md b/terra-jupyter-minimal-base/README.md new file mode 100644 index 00000000..ca65f377 --- /dev/null +++ b/terra-jupyter-minimal-base/README.md @@ -0,0 +1,30 @@ +# terra-jupyter-minimal-base image + +This repo contains the terra-jupyter-minimal-base image that is compatible with +notebook service in [Terra]("https://app.terra.bio/") called Leonardo. + +## Image contents + +`terra-jupyter-minimal-base` extends an Ubuntu 20.04 base image with the minimum +requirements necessary to set up Jupyter and provide compatibility with Leonardo. + +- OS prerequisites +- google-cloud-cli +- Python 3.7 +- Miniconda +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) +- Full list of python packages is available [here](requirements.txt) and + [here](requirements_gcc.txt) + +To see the complete contents of this image please see the [Dockerfile](./Dockerfile). + +## Selecting prior versions of this image + +To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) +for a specific package version you need. + +Once you find an image version that you want, simply copy and paste the image +url from the changelog into the corresponding custom docker field in the Terra +notebook runtime widget. diff --git a/terra-jupyter-minimal-gpu-base/CHANGELOG.md b/terra-jupyter-minimal-gpu-base/CHANGELOG.md new file mode 100644 index 00000000..9808f683 --- /dev/null +++ b/terra-jupyter-minimal-gpu-base/CHANGELOG.md @@ -0,0 +1,13 @@ +## 0.0.1 - 08/30/2022 + +- Extends `nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04` base image + - CUDA 11.3 + - Nvidia driver version 465.19.01 +- Add google-cloud-cli +- Add Python 3.7 +- Add Miniconda +- Add Jupyter +- Add Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + +Image URL: `us.gcr.io/broad-dsde-methods/terra-jupyter-minimal-gpu-base:0.0.1` diff --git a/terra-jupyter-minimal-gpu-base/README.md b/terra-jupyter-minimal-gpu-base/README.md new file mode 100644 index 00000000..45334de6 --- /dev/null +++ b/terra-jupyter-minimal-gpu-base/README.md @@ -0,0 +1,42 @@ +# terra-jupyter-minimal-gpu-base image + +This repo contains the terra-jupyter-minimal-gpu-base image that is compatible with +notebook service in [Terra]("https://app.terra.bio/") called Leonardo. + +## Image contents + +`terra-jupyter-minimal-gpu-base` extends an image from Nvidia built on top of +Ubuntu 20.04 called `nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04`, which has CUDA and +Nvidia drivers installed. This image adds the minimum +requirements necessary to set up Jupyter and provide compatibility with Leonardo. + +- CUDA 11.3 +- Nvidia driver version 465.19.01 +- OS prerequisites +- google-cloud-cli +- Python 3.7 +- Miniconda +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) +- Full list of python packages is available [here](requirements.txt) and + [here](requirements_gcc.txt) + +The philosophy here is minimalism, so that this image can be built upon. +To that end, neither `pytorch` nor `tensorflow` are pre-installed here, +so that will need to be done by images built on top of `terra-jupyter-minimal-gpu-base`. + +To see the complete contents of this image please see the +[Dockerfile](../terra-jupyter-minimal-base/Dockerfile) (the same Dockerfile as +`terra-jupyter-minimal-base`), +which builds `FROM nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04` +(the build command is [here](./build_docker.sh)). + +## Selecting prior versions of this image + +To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) +for a specific package version you need. + +Once you find an image version that you want, simply copy and paste the image +url from the changelog into the corresponding custom docker field in the Terra +notebook runtime widget. From 1877f5d07bea5e701a7e10d4437c1921b172475e Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Thu, 1 Sep 2022 16:41:53 -0400 Subject: [PATCH 08/26] Softlink google cloud cli tools to /usr/bin --- terra-jupyter-minimal-base/Dockerfile | 35 ++++++++++++++------- terra-jupyter-minimal-base/requirements.txt | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index ec277a56..da07fe18 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -18,12 +18,13 @@ ENV DEBIAN_FRONTEND=noninteractive \ JUPYTER_PORT=8000 \ JUPYTER_HOME=/etc/jupyter \ CONDA_AUTO_UPDATE_CONDA=false \ - CONDA_DIR=/opt/conda + CONDA_DIR=/opt/conda \ + GCLOUD_DIR=/opt/gcloud ENV HOME=/home/$USER # When using PIP_USER=true packages are installed into Python site.USER_BASE, which is '/home/jupyter' for this system. # Append '/home/jupyter/.local/bin' to PATH # pip docs: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-user -ENV PATH="${PATH}:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin:${HOME}/google-cloud-sdk/bin:" +ENV PATH="${GCLOUD_DIR}/google-cloud-sdk/bin:${CONDA_DIR}/bin:${HOME}/.local/bin:${HOME}/packages/bin:${PATH}" COPY requirements.txt /opt/ COPY requirements_gcc.txt /opt/ @@ -64,23 +65,33 @@ RUN useradd -m -s /bin/bash $USER \ # Uncomment en_US.UTF-8 for inclusion in generation && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ # Generate locale - && locale-gen \ + && sudo locale-gen \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ + && rm -rf /var/lib/apt/lists/* + # Install miniconda to $CONDA_DIR - && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ +RUN curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ - && rm $HOME/miniconda.sh \ + && rm $HOME/miniconda.sh + # Install gsutil with compiled crcmod - && curl -so $HOME/google-cloud-cli.tar.gz \ +RUN mkdir -p $GCLOUD_DIR \ + && curl -so $GCLOUD_DIR/google-cloud-cli.tar.gz \ https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-${GOOGLE_CLOUD_CLI_VERSION}-linux-x86_64.tar.gz \ - && tar -xzf $HOME/google-cloud-cli.tar.gz -C $HOME \ - && .$HOME/google-cloud-sdk/install.sh \ - && rm $HOME/google-cloud-cli.tar.gz \ - && rm -rf ~/.cache/pip \ + && tar -xzf $GCLOUD_DIR/google-cloud-cli.tar.gz -C $GCLOUD_DIR \ + && .$GCLOUD_DIR/google-cloud-sdk/install.sh --usage-reporting false --rc-path $HOME/.bashrc \ + # manual symlinks since .bashrc is not getting sourced and PATH is getting overwritten in Terra Cloud Environment + && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/gsutil /usr/bin/gsutil \ + && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/anthoscli /usr/bin/anthoscli \ + && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/bq /usr/bin/bq \ + && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/docker-credential-gcloud /usr/bin/docker-credential-gcloud \ + && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/gcloud /usr/bin/gcloud \ + # creates the directory $HOME/.config/gcloud/configurations + && gsutil version -l + # Slim install of python packages that have a gcc dependency (cleanup included) - && apt-get update && apt-get install -yq --no-install-recommends \ +RUN apt-get update && apt-get install -yq --no-install-recommends \ $(cat /opt/gcc_pkgs.txt) \ && pip3 install -r /opt/requirements_gcc.txt \ && apt-get purge -y --auto-remove \ diff --git a/terra-jupyter-minimal-base/requirements.txt b/terra-jupyter-minimal-base/requirements.txt index f2e668d0..6b68230b 100644 --- a/terra-jupyter-minimal-base/requirements.txt +++ b/terra-jupyter-minimal-base/requirements.txt @@ -6,6 +6,6 @@ python-datauri==1.1.0 jupyter_contrib_nbextensions==0.5.1 jupyter_nbextensions_configurator==0.5.0 markupsafe==2.0.1 -nbconvert>=6.4.5 +nbconvert==7.0.0 requests==2.27.1 crcmod==1.7 \ No newline at end of file From f4b7b41e57e4ff260df7f68531d94423ced96950 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Tue, 6 Sep 2022 13:14:34 -0400 Subject: [PATCH 09/26] Combined some steps to make image marginally smaller --- terra-jupyter-minimal-base/Dockerfile | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index da07fe18..85846fa8 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -63,20 +63,18 @@ RUN useradd -m -s /bin/bash $USER \ # git git \ # Uncomment en_US.UTF-8 for inclusion in generation - && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ + && sudo sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ # Generate locale && sudo locale-gen \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - + && rm -rf /var/lib/apt/lists/* \ # Install miniconda to $CONDA_DIR -RUN curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ + && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py37_4.12.0-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ - && rm $HOME/miniconda.sh - + && rm $HOME/miniconda.sh \ # Install gsutil with compiled crcmod -RUN mkdir -p $GCLOUD_DIR \ + && mkdir -p $GCLOUD_DIR \ && curl -so $GCLOUD_DIR/google-cloud-cli.tar.gz \ https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-${GOOGLE_CLOUD_CLI_VERSION}-linux-x86_64.tar.gz \ && tar -xzf $GCLOUD_DIR/google-cloud-cli.tar.gz -C $GCLOUD_DIR \ @@ -88,10 +86,9 @@ RUN mkdir -p $GCLOUD_DIR \ && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/docker-credential-gcloud /usr/bin/docker-credential-gcloud \ && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/gcloud /usr/bin/gcloud \ # creates the directory $HOME/.config/gcloud/configurations - && gsutil version -l - + && gsutil version -l \ # Slim install of python packages that have a gcc dependency (cleanup included) -RUN apt-get update && apt-get install -yq --no-install-recommends \ + && apt-get update && apt-get install -yq --no-install-recommends \ $(cat /opt/gcc_pkgs.txt) \ && pip3 install -r /opt/requirements_gcc.txt \ && apt-get purge -y --auto-remove \ From 6f39742abdebab4bb1e12ea01aaae5c04b26baf1 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Sat, 10 Sep 2022 02:03:15 -0400 Subject: [PATCH 10/26] Important space saver: move chown commands up chown on a large folder copies all the files in the folder, essentially doubling the image size. Also conda clean -yaf is better than -ya --- terra-jupyter-minimal-base/Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 85846fa8..5a1e2bbc 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -87,6 +87,11 @@ RUN useradd -m -s /bin/bash $USER \ && ln -s $GCLOUD_DIR/google-cloud-sdk/bin/gcloud /usr/bin/gcloud \ # creates the directory $HOME/.config/gcloud/configurations && gsutil version -l \ + && chown -R $USER:users $HOME/.conda \ + && chown -R $USER:users $CONDA_DIR \ + && chown -R $USER:users $HOME/.config/gcloud \ + && mkdir $JUPYTER_HOME \ + && chown -R $USER:users $JUPYTER_HOME \ # Slim install of python packages that have a gcc dependency (cleanup included) && apt-get update && apt-get install -yq --no-install-recommends \ $(cat /opt/gcc_pkgs.txt) \ @@ -112,18 +117,14 @@ RUN useradd -m -s /bin/bash $USER \ https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ && pip3 install $HOME/cromshell \ && rm -rf ~/.cache/pip \ - && conda clean -ya + && conda clean -yaf # Utilities COPY scripts $JUPYTER_HOME/scripts COPY custom $JUPYTER_HOME/custom COPY jupyter_notebook_config.py $JUPYTER_HOME -RUN chown -R $USER:users $JUPYTER_HOME \ - && chown -R $USER:users $HOME/.conda \ - && chown -R $USER:users $CONDA_DIR \ - && chown -R $USER:users $HOME/.config/gcloud \ - && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ +RUN find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ # You can get kernel directory by running `jupyter kernelspec list` && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels From 499e5e3580c4514428f4cffbffdc201aaa696cbc Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 16 Sep 2022 12:14:49 -0400 Subject: [PATCH 11/26] Add notes about WORKSPACE_BUCKET env var to readme --- terra-jupyter-minimal-base/README.md | 8 ++++++++ terra-jupyter-minimal-gpu-base/CHANGELOG.md | 1 - terra-jupyter-minimal-gpu-base/README.md | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/terra-jupyter-minimal-base/README.md b/terra-jupyter-minimal-base/README.md index ca65f377..6f904f28 100644 --- a/terra-jupyter-minimal-base/README.md +++ b/terra-jupyter-minimal-base/README.md @@ -20,6 +20,14 @@ requirements necessary to set up Jupyter and provide compatibility with Leonardo To see the complete contents of this image please see the [Dockerfile](./Dockerfile). +## Notes + +- Currently, the environment variable `WORKSPACE_BUCKET` is not getting set + correctly when this image is used to create a Cloud Environment in Terra + - The value is `'.'` + - This will be fixed in future versions + - For now, it's recommended to avoid using this environment variable + ## Selecting prior versions of this image To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) diff --git a/terra-jupyter-minimal-gpu-base/CHANGELOG.md b/terra-jupyter-minimal-gpu-base/CHANGELOG.md index 9808f683..de618108 100644 --- a/terra-jupyter-minimal-gpu-base/CHANGELOG.md +++ b/terra-jupyter-minimal-gpu-base/CHANGELOG.md @@ -2,7 +2,6 @@ - Extends `nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04` base image - CUDA 11.3 - - Nvidia driver version 465.19.01 - Add google-cloud-cli - Add Python 3.7 - Add Miniconda diff --git a/terra-jupyter-minimal-gpu-base/README.md b/terra-jupyter-minimal-gpu-base/README.md index 45334de6..2a160baa 100644 --- a/terra-jupyter-minimal-gpu-base/README.md +++ b/terra-jupyter-minimal-gpu-base/README.md @@ -22,16 +22,24 @@ requirements necessary to set up Jupyter and provide compatibility with Leonardo - Full list of python packages is available [here](requirements.txt) and [here](requirements_gcc.txt) -The philosophy here is minimalism, so that this image can be built upon. -To that end, neither `pytorch` nor `tensorflow` are pre-installed here, -so that will need to be done by images built on top of `terra-jupyter-minimal-gpu-base`. - To see the complete contents of this image please see the [Dockerfile](../terra-jupyter-minimal-base/Dockerfile) (the same Dockerfile as `terra-jupyter-minimal-base`), which builds `FROM nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04` (the build command is [here](./build_docker.sh)). +## Notes + +- The philosophy here is minimalism, so that this image can be built upon. +To that end, neither `pytorch` nor `tensorflow` are pre-installed here, +so that will need to be done by images built on top of `terra-jupyter-minimal-gpu-base`. + +- Currently, the environment variable `WORKSPACE_BUCKET` is not getting set + correctly when this image is used to create a Cloud Environment in Terra + - The value is `'.'` + - This will be fixed in future versions + - For now, it's recommended to avoid using this environment variable + ## Selecting prior versions of this image To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) From eeb85192b6eacc6c8af09e3a9f83986227cfd8a5 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 16 Sep 2022 12:16:57 -0400 Subject: [PATCH 12/26] Remove typo from README --- terra-jupyter-minimal-gpu-base/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/terra-jupyter-minimal-gpu-base/README.md b/terra-jupyter-minimal-gpu-base/README.md index 2a160baa..0861e683 100644 --- a/terra-jupyter-minimal-gpu-base/README.md +++ b/terra-jupyter-minimal-gpu-base/README.md @@ -11,7 +11,6 @@ Nvidia drivers installed. This image adds the minimum requirements necessary to set up Jupyter and provide compatibility with Leonardo. - CUDA 11.3 -- Nvidia driver version 465.19.01 - OS prerequisites - google-cloud-cli - Python 3.7 From 13f21916b5536d45f2ba9ea0a312aee21799d771 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 16 Sep 2022 15:58:41 -0400 Subject: [PATCH 13/26] Fix $JUPYTER_HOME permissions issue --- terra-jupyter-minimal-base/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 5a1e2bbc..cedce193 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -90,8 +90,6 @@ RUN useradd -m -s /bin/bash $USER \ && chown -R $USER:users $HOME/.conda \ && chown -R $USER:users $CONDA_DIR \ && chown -R $USER:users $HOME/.config/gcloud \ - && mkdir $JUPYTER_HOME \ - && chown -R $USER:users $JUPYTER_HOME \ # Slim install of python packages that have a gcc dependency (cleanup included) && apt-get update && apt-get install -yq --no-install-recommends \ $(cat /opt/gcc_pkgs.txt) \ @@ -124,7 +122,8 @@ COPY scripts $JUPYTER_HOME/scripts COPY custom $JUPYTER_HOME/custom COPY jupyter_notebook_config.py $JUPYTER_HOME -RUN find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ +RUN chown -R $USER:users $JUPYTER_HOME \ + && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ # You can get kernel directory by running `jupyter kernelspec list` && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel $CONDA_DIR/share/jupyter/kernels From 6afdba0b8a6278942edf09c1720facbbded89164 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Fri, 16 Sep 2022 18:02:17 -0400 Subject: [PATCH 14/26] Improve permissions for user installs via pip and conda --- terra-jupyter-minimal-base/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index cedce193..b2be2c59 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -88,7 +88,6 @@ RUN useradd -m -s /bin/bash $USER \ # creates the directory $HOME/.config/gcloud/configurations && gsutil version -l \ && chown -R $USER:users $HOME/.conda \ - && chown -R $USER:users $CONDA_DIR \ && chown -R $USER:users $HOME/.config/gcloud \ # Slim install of python packages that have a gcc dependency (cleanup included) && apt-get update && apt-get install -yq --no-install-recommends \ @@ -115,7 +114,8 @@ RUN useradd -m -s /bin/bash $USER \ https://github.com/broadinstitute/cromshell.git $HOME/cromshell \ && pip3 install $HOME/cromshell \ && rm -rf ~/.cache/pip \ - && conda clean -yaf + && conda clean -yaf \ + && chown -R $USER:users $CONDA_DIR # Utilities COPY scripts $JUPYTER_HOME/scripts From 9339f5e990377b4de08bccc51bad7644fa964df3 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Thu, 31 Aug 2023 01:02:00 -0400 Subject: [PATCH 15/26] Update to python 3.10 and update package versions (#1) * Python 3.10 and suggested updates from Ray Jones; install mamba * Bump to version 0.0.2 * Update google-cloud-cli version * Install compiled crcmod * Update package versions and install libarchive for mamba --- .github/workflows/publish.yml | 55 +++ .github/workflows/pull_request_template.md | 16 + .github/workflows/test-terra-jupyter-aou.yml | 14 +- .github/workflows/test-terra-jupyter-base.yml | 12 +- .../test-terra-jupyter-bioconductor.yml | 8 +- .../test-terra-jupyter-gatk-ovtf.yml | 126 ----- .github/workflows/test-terra-jupyter-gatk.yml | 14 +- .github/workflows/test-terra-jupyter-hail.yml | 12 +- .../workflows/test-terra-jupyter-python.yml | 18 +- .gitignore | 4 +- CONTRIBUTING.md | 2 + README.md | 51 +- build.sh | 34 +- build_smoke_test_image.sh | 2 +- config/community_images.json | 24 +- config/conf.json | 65 ++- config/legacy_static_images.json | 19 +- jenkins/Jenkinsfile | 2 +- scripts/generate_package_docs.py | 9 +- terra-jupyter-aou/CHANGELOG.md | 118 +++++ terra-jupyter-aou/Dockerfile | 133 ++++-- terra-jupyter-aou/tests/aou_smoke_test.ipynb | 2 +- terra-jupyter-base/CHANGELOG.md | 25 + terra-jupyter-base/Dockerfile | 86 ++-- terra-jupyter-base/README.md | 2 +- terra-jupyter-base/tests/smoke_test.py | 1 + terra-jupyter-bioconductor/CHANGELOG.md | 48 ++ terra-jupyter-bioconductor/Dockerfile | 11 +- .../tests/smoke_test.ipynb | 240 ++++++++-- terra-jupyter-gatk-ovtf/CHANGELOG.md | 107 ----- terra-jupyter-gatk-ovtf/Dockerfile | 70 --- .../GATK-OVTF-Notebook.ipynb | 104 ----- terra-jupyter-gatk-ovtf/README.md | 99 ---- terra-jupyter-gatk-ovtf/build.sh | 4 - terra-jupyter-gatk-ovtf/gatk-ovtf.patch | 104 ----- .../ovtf-cuda-disabling.patch | 12 - terra-jupyter-gatk-ovtf/run_gatk.sh | 10 - terra-jupyter-gatk-ovtf/terra-ovtf-gatk.png | Bin 257491 -> 0 bytes .../tests/ovtf_smoke_test.ipynb | 144 ------ .../tests/ovtf_smoke_test.py | 22 - terra-jupyter-gatk/CHANGELOG.md | 61 +++ terra-jupyter-gatk/Dockerfile | 39 +- .../tests/gatk_smoke_test.ipynb | 81 ++-- terra-jupyter-hail/CHANGELOG.md | 63 +++ terra-jupyter-hail/Dockerfile | 17 +- .../tests/hail_smoke_test.ipynb | 2 +- terra-jupyter-minimal-base/Dockerfile | 17 +- terra-jupyter-minimal-base/build_docker.sh | 2 +- terra-jupyter-minimal-base/requirements.txt | 21 +- .../requirements_gcc.txt | 5 +- .../build_docker.sh | 4 +- terra-jupyter-python/CHANGELOG.md | 26 ++ terra-jupyter-python/Dockerfile | 33 +- terra-jupyter-python/requirements.txt | 6 +- terra-jupyter-python/tests/smoke_test.ipynb | 41 +- terra-jupyter-python/tests/smoke_test.py | 29 +- terra-jupyter-r/CHANGELOG.md | 44 ++ terra-jupyter-r/Dockerfile | 41 +- terra-jupyter-r/tests/smoke_test.ipynb | 437 ++++++++++++++++++ terra-rstudio-aou/CHANGELOG.md | 9 + terra-rstudio-aou/Dockerfile | 65 +++ terra-rstudio-aou/README.md | 19 + terra-rstudio-aou/VERSION | 1 + terra-rstudio-aou/rstudio/entrypoint.sh | 15 + terra-rstudio-aou/rstudio/rserver.conf | 7 + .../rstudio/scripts/install_bioconductor.R | 50 ++ .../rstudio/scripts/install_tidyverse.sh | 75 +++ terra-rstudio-aou/rstudio/tests/smoke_test.R | 73 +++ updateVersions.sc | 14 +- wondershaper/Dockerfile | 22 + wondershaper/README.md | 38 ++ wondershaper/entrypoint.sh | 12 + 72 files changed, 2021 insertions(+), 1177 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/pull_request_template.md delete mode 100644 .github/workflows/test-terra-jupyter-gatk-ovtf.yml delete mode 100644 terra-jupyter-gatk-ovtf/CHANGELOG.md delete mode 100644 terra-jupyter-gatk-ovtf/Dockerfile delete mode 100644 terra-jupyter-gatk-ovtf/GATK-OVTF-Notebook.ipynb delete mode 100644 terra-jupyter-gatk-ovtf/README.md delete mode 100755 terra-jupyter-gatk-ovtf/build.sh delete mode 100644 terra-jupyter-gatk-ovtf/gatk-ovtf.patch delete mode 100644 terra-jupyter-gatk-ovtf/ovtf-cuda-disabling.patch delete mode 100644 terra-jupyter-gatk-ovtf/run_gatk.sh delete mode 100644 terra-jupyter-gatk-ovtf/terra-ovtf-gatk.png delete mode 100644 terra-jupyter-gatk-ovtf/tests/ovtf_smoke_test.ipynb delete mode 100644 terra-jupyter-gatk-ovtf/tests/ovtf_smoke_test.py create mode 100644 terra-jupyter-r/tests/smoke_test.ipynb create mode 100644 terra-rstudio-aou/CHANGELOG.md create mode 100644 terra-rstudio-aou/Dockerfile create mode 100644 terra-rstudio-aou/README.md create mode 100644 terra-rstudio-aou/VERSION create mode 100644 terra-rstudio-aou/rstudio/entrypoint.sh create mode 100644 terra-rstudio-aou/rstudio/rserver.conf create mode 100644 terra-rstudio-aou/rstudio/scripts/install_bioconductor.R create mode 100644 terra-rstudio-aou/rstudio/scripts/install_tidyverse.sh create mode 100755 terra-rstudio-aou/rstudio/tests/smoke_test.R create mode 100644 wondershaper/Dockerfile create mode 100644 wondershaper/README.md create mode 100644 wondershaper/entrypoint.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..cb569f9f --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,55 @@ +name: Publish image to GCR +on: + workflow_dispatch: + inputs: + image: + description: 'Image to publish' + required: true + type: choice + options: + - terra-jupyter-aou + - terra-jupyter-base + - terra-jupyter-bioconductor + - terra-jupyter-gatk + - terra-jupyter-hail + - terra-jupyter-python + - terra-jupyter-r + - terra-rstudio-aou + - wondershaper + default: terra-rstudio-rstudio + branch: + description: 'Branch to run the workflow on' + required: false + default: 'master' +jobs: + publish-job: + runs-on: self-hosted + steps: + - name: Checkout current code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - id: auth + uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} + create_credentials_file: true + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v0.3.0 + with: + project_id: ${{ secrets.GCP_PROJECT_ID }} + + - name: Explicitly auth Docker for GCR + run: gcloud auth configure-docker --quiet + + - name: Build Docker image and publish + run: | + gcloud auth configure-docker + ./build.sh ${{ github.event.inputs.image }} "true" diff --git a/.github/workflows/pull_request_template.md b/.github/workflows/pull_request_template.md new file mode 100644 index 00000000..7fadd2cc --- /dev/null +++ b/.github/workflows/pull_request_template.md @@ -0,0 +1,16 @@ +### Jira Ticket: https://broadworkbench.atlassian.net/browse/[Ticket #] + + + + +## Summary of changes: + + +### What +- + +### Why +- + +### Testing strategy +- [ ] \ No newline at end of file diff --git a/.github/workflows/test-terra-jupyter-aou.yml b/.github/workflows/test-terra-jupyter-aou.yml index 39c2eb59..dd38e95e 100644 --- a/.github/workflows/test-terra-jupyter-aou.yml +++ b/.github/workflows/test-terra-jupyter-aou.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-aou # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -38,24 +38,24 @@ env: jobs: test_docker_image: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK diff --git a/.github/workflows/test-terra-jupyter-base.yml b/.github/workflows/test-terra-jupyter-base.yml index a0599946..ce34ceb6 100644 --- a/.github/workflows/test-terra-jupyter-base.yml +++ b/.github/workflows/test-terra-jupyter-base.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-base # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -42,20 +42,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK diff --git a/.github/workflows/test-terra-jupyter-bioconductor.yml b/.github/workflows/test-terra-jupyter-bioconductor.yml index c02afadd..e90e61e1 100644 --- a/.github/workflows/test-terra-jupyter-bioconductor.yml +++ b/.github/workflows/test-terra-jupyter-bioconductor.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-bioconductor # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -47,15 +47,15 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK diff --git a/.github/workflows/test-terra-jupyter-gatk-ovtf.yml b/.github/workflows/test-terra-jupyter-gatk-ovtf.yml deleted file mode 100644 index fb5fa6a5..00000000 --- a/.github/workflows/test-terra-jupyter-gatk-ovtf.yml +++ /dev/null @@ -1,126 +0,0 @@ -name: Test terra-jupyter-gatk-ovtf -# Perform smoke tests on the terra-jupyter-gatk-ovtf Docker image to have some amount of confidence that -# Python package versions are compatible. -# -# To configure the minimal auth needed for these tests to be able to read public data from Google Cloud Platform: -# Step 1: Create a service account per these instructions: -# https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md -# Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. -# https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository - -on: - pull_request: - branches: [ master ] - paths: - - 'terra-jupyter-gatk-ovtf/**' - - '.github/workflows/test-terra-jupyter-gatk-ovtf.yml' - - push: - # Note: GitHub secrets are not passed to pull requests from forks. For community contributions from - # regular contributors, its a good idea for the contributor to configure the GitHub actions to run correctly - # in their fork as described above. - # - # For occasional contributors, the dev team will merge the PR fork branch to a branch in upstream named - # test-community-contribution- to run all the GitHub Action smoke tests. - branches: [ 'test-community-contribution*' ] - paths: - - 'terra-jupyter-gatk-ovtf/**' - - '.github/workflows/test-terra-jupyter-gatk-ovtf.yml' - - workflow_dispatch: - # Allows manually triggering of workflow on a selected branch via the GitHub Actions tab. - # GitHub blog demo: https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/. - -env: - GOOGLE_PROJECT: ${{ secrets.GCP_PROJECT_ID }} - -jobs: - - test_docker_image: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - - name: Free up some disk space - run: sudo rm -rf /usr/share/dotnet - - - id: auth - uses: google-github-actions/auth@v0 - with: - credentials_json: ${{ secrets.GCP_SA_KEY }} - create_credentials_file: true - - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v0.3.0 - with: - project_id: ${{ secrets.GCP_PROJECT_ID }} - - - name: Build Docker image and base images too, if needed - run: | - gcloud auth configure-docker - ./build_smoke_test_image.sh terra-jupyter-gatk-ovtf - - - name: Run Python code specific to notebooks with nbconvert - # Run all notebooks from start to finish, regardles of error, so that we can capture the - # result as a workflow artifact. - # See also https://github.com/marketplace/actions/run-notebook if a more complicated - # workflow for notebooks is needed in the future. - run: | - chmod a+w -R $GITHUB_WORKSPACE - chmod a+r "${{ steps.auth.outputs.credentials_file_path }}" - docker run \ - --env GOOGLE_PROJECT \ - --volume "${{ steps.auth.outputs.credentials_file_path }}":/tmp/credentials.json:ro \ - --env GOOGLE_APPLICATION_CREDENTIALS="/tmp/credentials.json" \ - --volume $GITHUB_WORKSPACE:/tests \ - --workdir=/tests \ - --entrypoint="" \ - terra-jupyter-gatk-ovtf:smoke-test \ - /bin/bash -c 'for nb in {terra-jupyter-python/tests,terra-jupyter-gatk-ovtf/tests}/*ipynb ; do jupyter nbconvert --to html --ExecutePreprocessor.allow_errors=True --execute "${nb}" ; done' - - - name: Upload workflow artifacts - uses: actions/upload-artifact@v2 - with: - name: notebook-execution-results - path: | - terra-jupyter-python/tests/*.html - terra-jupyter-gatk-ovtf/tests/*.html - retention-days: 30 - - - name: Test Python code with pytest - run: | - chmod a+r "${{ steps.auth.outputs.credentials_file_path }}" - docker run \ - --env GOOGLE_PROJECT \ - --volume "${{ steps.auth.outputs.credentials_file_path }}":/tmp/credentials.json:ro \ - --env GOOGLE_APPLICATION_CREDENTIALS="/tmp/credentials.json" \ - --volume $GITHUB_WORKSPACE:/tests \ - --workdir=/tests \ - --entrypoint="" \ - terra-jupyter-gatk-ovtf:smoke-test \ - /bin/bash -c 'pip3 install pytest ; pytest terra-jupyter-python/tests/ terra-jupyter-gatk-ovtf/tests/' - - - name: Test Python code specific to notebooks with nbconvert - # Simply 'Cell -> Run All` these notebooks and expect no errors in the case of a successful run of the test suite. - # If the tests throw any exceptions, execution of the notebooks will halt at that point. Look at the workflow - # artifacts to understand if there are more failures than just the one that caused this task to halt. - run: | - chmod a+r "${{ steps.auth.outputs.credentials_file_path }}" - docker run \ - --env GOOGLE_PROJECT \ - --volume "${{ steps.auth.outputs.credentials_file_path }}":/tmp/credentials.json:ro \ - --env GOOGLE_APPLICATION_CREDENTIALS="/tmp/credentials.json" \ - --volume $GITHUB_WORKSPACE:/tests \ - --workdir=/tests \ - --entrypoint="" \ - terra-jupyter-gatk-ovtf:smoke-test \ - /bin/bash -c 'for nb in {terra-jupyter-python/tests,terra-jupyter-gatk-ovtf/tests}/*ipynb ; do jupyter nbconvert --to html --execute "${nb}" ; done' - diff --git a/.github/workflows/test-terra-jupyter-gatk.yml b/.github/workflows/test-terra-jupyter-gatk.yml index edd9cee7..aa911c8e 100644 --- a/.github/workflows/test-terra-jupyter-gatk.yml +++ b/.github/workflows/test-terra-jupyter-gatk.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-gatk # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -38,24 +38,24 @@ env: jobs: test_docker_image: - runs-on: ubuntu-latest + runs-on: self-hosted steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK diff --git a/.github/workflows/test-terra-jupyter-hail.yml b/.github/workflows/test-terra-jupyter-hail.yml index 27e35a7c..1a5a9307 100644 --- a/.github/workflows/test-terra-jupyter-hail.yml +++ b/.github/workflows/test-terra-jupyter-hail.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-hail # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -42,20 +42,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK diff --git a/.github/workflows/test-terra-jupyter-python.yml b/.github/workflows/test-terra-jupyter-python.yml index a21cc6fd..f72fafaf 100644 --- a/.github/workflows/test-terra-jupyter-python.yml +++ b/.github/workflows/test-terra-jupyter-python.yml @@ -6,7 +6,7 @@ name: Test terra-jupyter-python # Step 1: Create a service account per these instructions: # https://github.com/google-github-actions/setup-gcloud/blob/master/setup-gcloud/README.md # Step 2: Give the service account the following permissions within the project: BigQuery User -# Step 3: Store its key and project id as GitHub repository secrets GCP_SA_KEY and GCP_PROJECT_ID. +# Step 3: Store its key and project id as GitHub repository secrets TD_GCP_SA_KEY and GCP_PROJECT_ID. # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository on: @@ -44,20 +44,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.10' - name: Free up some disk space run: sudo rm -rf /usr/share/dotnet - id: auth - uses: google-github-actions/auth@v0 + uses: google-github-actions/auth@v1 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + credentials_json: ${{ secrets.TD_GCP_SA_KEY }} create_credentials_file: true - name: Set up Cloud SDK @@ -112,10 +112,8 @@ jobs: --workdir=/tests \ --entrypoint="" \ terra-jupyter-python:smoke-test \ - /bin/sh -c "pip3 install --no-deps pytest iniconfig pluggy py; pytest" - # Use --no-deps to avoid an unneccessary upgrade of pyparsing which then causes tensorflow tests to fail. - # TODO: whenever we no longer need to pin pyparsing, simplify the above command to 'pip3 install pytest ; pytest' - + /bin/sh -c "pip3 install pytest ; pytest" + - name: Test Python code specific to notebooks with nbconvert # Simply 'Cell -> Run All` these notebooks and expect no errors in the case of a successful run of the test suite. # If the tests throw any exceptions, execution of the notebooks will halt at that point. Look at the workflow diff --git a/.gitignore b/.gitignore index 4f7c29c6..c7b1ac97 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ package-lock.json .bash_history .conda/ .python_history -.keras/ \ No newline at end of file +.keras/ +.ammonite/ +.metals/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6000fdfb..65f4b446 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,8 @@ If you are from outside the Broad, don't worry about adding a JIRA issue number If you are handling these contributions from the Broad side, please remember to create a corresponding JIRA ticket. This ticket number will be used to track contributions internally. +Standard operating procedures between Bioconductor and Terra teams during release have been detailed in [this document](https://docs.google.com/document/d/1-TVfD9GisifdgB9rjM5Q2ieCri1NPEC8cgHZTy8fWeo/edit#). + ## Community Images Community images must be vetted before they will be introduced into Terra. diff --git a/README.md b/README.md index 8ee610c0..e96bf63b 100644 --- a/README.md +++ b/README.md @@ -57,28 +57,48 @@ docker run -d -p :8000 -v /terra-docke Once you have the container running, you should be able to access jupyter at http://localhost:/notebooks. You should be able to navigate to the smoke test ipynb file you're interested in, and run a cell. After you modify a smoke test `.ipynb` file, go to `Cell` -> `All Ouput` -> `Clear` to clear all outputs to keep the `.ipynb` files smaller. -## Generate New Image -If you are adding a new image: +## Generate New Image or Update Existing Image + +Detailed documentation on how to integrate the terra-docker image with Leonardo can be found [here](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo) + +### If you are adding a new image: - Create a new directory with the Dockerfile and a CHANGELOG.md. - Add the directory name (also referred to as the image name) as an entry to the image_data array in the file in config/conf.json. For more info on what is needed for a new image, see the section on the config - If you wish the image to be baked into our custom image, which makes the runtime load significantly faster (recommended), make a PR into the leonardo [repo](https://github.com/DataBiosphere/leonardo) doing the following within the `jenkins` folder: - Add the image to the parameter list in the Jenkinsfile - Update the relevant `prepare` script in each subdirectory. Currently there is a prepare script for gce and dataproc. - - It is recommended to add a test in the `automation` directory + - It is recommended to add a test in the `automation` directory (`automation/src/test/resources/reference.conf`) - Add your image to the `reference.conf` in the automation directory. This will be the only place any future version updates to your image happen. This ensures, along with the test in the previous step, that any changes to the image are tested. + - Run the GHA to generate the image, and add it to `reference.conf` in the http directory (`http/src/main/resources/reference.conf`) + +### If you are updating an existing image: +- [Create your terra-docker PR](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#1.-Create-a-terra-docker-PR) + - Update the version in config/conf.json + - Update CHANGELOG.md and VERSION file + - Ensure that no `From` statements need to be updated based on the image you updated (i.e., if you update the base image, you will need to update several other images) + - Run updateVersions.sc to bump all images dependent on the base +- [Merge your terra-docker PR and check if the image(s) and version json files are created](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#2.-Merge-your-terra-docker-PR-and-check-images-are-created) +- [Open a PR in leonardo](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#3.-Create-a-new-leo-PR-that-integrates-the-new-images) + - Update the relevant `prepare` script within the `jenkins` folder + - Update the automation `reference.conf` file +- [Run the GHA on your branch to generate the new image](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#4.-Run-the-Github-Action-in-leo-to-generate-a-new-custom-COS-image) +- [Update the leonardo PR to use the newly generated image](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#5.-Update-the-Leo-PR-to-use-the-generated-OS-images) +- Ensure that the `terra-docker-versions-candidate.json` file (which is what the UI sources the dropdown from) in the `terra-docjker-image-documentation-[env]` bucket correclty references your new docker image +- [Update the terra-docker version candidate json](https://broadworkbench.atlassian.net/wiki/spaces/IA/pages/2519564289/Integrating+new+Terra+docker+images+with+Leonardo#6.-Update-terra-docker-versions-candidate.json) -If you are updating an existing image: -- Update the version in config/conf.json -- Update CHANGELOG.md -- Ensure that no `From` statements need to be updated based on the image you updated (i.e., if you update the base image, you will need to update several other images) -- Follow [instructions](https://broadworkbench.atlassian.net/wiki/spaces/AP/pages/100401153/Testing+notebook+functionality+with+Fiab) to test the image -- Once you merge a PR, A [jenkins job](https://fc-jenkins.dsp-techops.broadinstitute.org/job/leonardo-build-terra-docker/) will automatically detect which image you have updated and build it + generate documentation. +## Testing your image manually -If you wish to build locally, run `docker build [your_dir] -t [name]`. +Build the image: +run `docker build [your_dir] -t [name]`. -It is not advised to run build.sh locally, as this will push to the remote docker repo and delete the image locally upon completion. +`docker build terra-jupyter-base -t terra-jupyter-base` + +If you're on an M1 and building an image from a locally built image, replace the current FROM command: + +`FROM --platform=linux/amd64 terra-jupyter-base` + +It is not advised to run build.sh locally, as this will push to the remote docker repo and delete the image locally upon completion. -## Testing your image manually All images can be run locally. For example: ``` docker run --rm -it -p 8000:8000 us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:0.0.7 @@ -95,7 +115,7 @@ Running locally is conventient for quick development and exploring the image. Ho - there are no environment variables like `GOOGLE_PROJECT`, `WORKSPACE_NAME`, `WORKSPACE_BUCKET`, etc when running locally - there is no workspace-syncing when run locally -To launch an image through Terra, navigate to https://app.terra.bio, select a workspace, enter your image in the "Custom Image" field, and click Create. +To launch an image through Terra, navigate to https://app.terra.bio or your BEE's UI, select a workspace, enter your new image in the "Custom Image" field, and click Create. ## Automation Tests [Here](https://github.com/DataBiosphere/leonardo/tree/develop/automation/src/test/scala/org/broadinstitute/dsde/workbench/leonardo/notebooks) are automation tests for various docker image, please update the image hash for relevant tests. You can run the job build-terra-docker to automatically create a PR with your branch if you manually specify versions. @@ -149,4 +169,9 @@ The scripts folder has scripts used for building. ## Image dependencies +Note that this dependency graph needs to be updated! ![Image dependencies](dependencies.png) + +## Push Images to GCR + +To push images to Broad managed Google Container Registry``gcr.io/broad-dsp-gcr-public``, manually trigger ``Publish image to GCR `` Github action and choose the image to push. \ No newline at end of file diff --git a/build.sh b/build.sh index 3d070f13..1f1a87b6 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,7 @@ set -e -x IMAGE_DIR=$1 +SKIP_GCP_AUTH=$2 VERSION=$(cat config/conf.json | jq -r ".image_data | .[] | select(.name == \"$IMAGE_DIR\") | .version") TAG_NAME=$(git log --pretty=format:'%h' -n 1) @@ -25,13 +26,16 @@ if [[ $VAULT_LOCATION == *"jenkins"* ]]; then VAULT_LOCATION="/etc/vault-token-dsde" fi -# will fail if you are not gcloud authed as dspci-wb-gcr-service-account or secret/dsde/firecloud/common/image-build-account.json -# the below works locally but not on jenkins for some reason, using a different account until a resolution is found -# docker run --rm -v $VAULT_LOCATION:/root/.vault-token:ro broadinstitute/dsde-toolbox:latest vault read --format=json secret/dsde/dsp-techops/common/dspci-wb-gcr-service-account.json | jq .data > dspci-wb-gcr-service-account.json -# gcloud auth activate-service-account --key-file=dspci-wb-gcr-service-account.json -docker run --rm -v $VAULT_LOCATION:/root/.vault-token:ro broadinstitute/dsde-toolbox:latest vault read --format=json secret/dsde/firecloud/common/image-build-account.json | jq .data > image-build-account.json -gcloud auth activate-service-account --key-file=image-build-account.json -gcloud auth configure-docker --quiet +if [[ -z "${SKIP_GCP_AUTH}" ]] || [["${SKIP_GCP_AUTH}" != "true"]]; then + # will fail if you are not gcloud authed as dspci-wb-gcr-service-account or secret/dsde/firecloud/common/image-build-account.json + # the below works locally but not on jenkins for some reason, using a different account until a resolution is found + # docker run --rm -v $VAULT_LOCATION:/root/.vault-token:ro broadinstitute/dsde-toolbox:latest vault read --format=json secret/dsde/dsp-techops/common/dspci-wb-gcr-service-account.json | jq .data > dspci-wb-gcr-service-account.json + # gcloud auth activate-service-account --key-file=dspci-wb-gcr-service-account.json + docker run --rm -v $VAULT_LOCATION:/root/.vault-token:ro broadinstitute/dsde-toolbox:latest vault read --format=json secret/dsde/firecloud/common/image-build-account.json | jq .data > image-build-account.json + gcloud auth activate-service-account --key-file=image-build-account.json + gcloud auth configure-docker --quiet +fi + docker image build ./$IMAGE_DIR \ --tag $GCR_IMAGE_REPO/$IMAGE_DIR:$TAG_NAME \ @@ -42,22 +46,14 @@ docker push $GCR_IMAGE_REPO/$IMAGE_DIR:$TAG_NAME docker push $GCR_IMAGE_REPO/$IMAGE_DIR:$VERSION docker push $GCR_IMAGE_REPO/$IMAGE_DIR:latest -if [[ $IMAGE_DIR = "terra-jupyter-aou" ]]; then - docker tag $GCR_IMAGE_REPO/$IMAGE_DIR:$TAG_NAME broadinstitute/$IMAGE_DIR:$TAG_NAME - docker tag $GCR_IMAGE_REPO/$IMAGE_DIR:$VERSION broadinstitute/$IMAGE_DIR:$VERSION - docker tag $GCR_IMAGE_REPO/$IMAGE_DIR:$VERSION broadinstitute/$IMAGE_DIR:latest - - docker push broadinstitute/$IMAGE_DIR:$VERSION - docker push broadinstitute/$IMAGE_DIR:$TAG_NAME - docker push broadinstitute/$IMAGE_DIR:latest -fi - docker kill $IMAGE_DIR || true docker rm -f $IMAGE_DIR || true docker run --rm -itd -u root -e PIP_USER=false --entrypoint='/bin/bash' --name $IMAGE_DIR $GCR_IMAGE_REPO/$IMAGE_DIR:$VERSION -gcloud auth list -python scripts/generate_package_docs.py "$IMAGE_DIR" +if [[ $IMAGE_DIR != "terra-rstudio-aou" ]]; then + gcloud auth list + python scripts/generate_package_docs.py "$IMAGE_DIR" +fi docker kill $IMAGE_DIR || true docker rm -f $IMAGE_DIR || true diff --git a/build_smoke_test_image.sh b/build_smoke_test_image.sh index d8e98ebe..aacf44f9 100755 --- a/build_smoke_test_image.sh +++ b/build_smoke_test_image.sh @@ -1,7 +1,7 @@ #!/bin/bash # Perform a local build of an image for smoke-testing purposes. Also build its tree of base images, when missing. # -# Example: ./build_smoke_test_image terra-jupyter-python +# Example: ./build_smoke_test_image.sh terra-jupyter-python # # Current working directory must be the directory in which this script resides (one level above all the Dockerfiles). # It walks down each relevant image directory, making changes to the FROM statements, diff --git a/config/community_images.json b/config/community_images.json index 040229b7..4d36ef18 100644 --- a/config/community_images.json +++ b/config/community_images.json @@ -10,21 +10,21 @@ }, { "id": "RStudio", - "label": "RStudio (R 4.2.0, Bioconductor 3.15, Python 3.8.10)", - "version": "3.15.2", - "updated": "2022-05-09", - "packages": "https://storage.googleapis.com/terra-docker-image-documentation/placeholder.json", - "image": "us.gcr.io/broad-dsp-gcr-public/anvil-rstudio-bioconductor:3.15.2", + "label": "RStudio (R 4.3.0, Bioconductor 3.17, Python 3.10.6)", + "version": "3.17.0", + "updated": "2023-05-12", + "packages": "https://raw.githubusercontent.com/anvilproject/anvil-docker/master/anvil-rstudio-bioconductor/info/anvil-rstudio-bioconductor-3.17.0-versions.json", + "image": "us.gcr.io/broad-dsp-gcr-public/anvil-rstudio-bioconductor:3.17.0", "requiresSpark": false, "isRStudio": true }, { - "id": "OpenVINO integration with Tensorflow", - "label": "OpenVINO integration with Tensorflow (openvino-tensorflow 1.1.0, Python 3.7.12, GATK 4.2.4.1)", - "version": "0.2.0", - "updated": "2022-01-31", - "packages": "https://storage.googleapis.com/terra-docker-image-documentation/terra-jupyter-gatk-ovtf-0.1.7-versions.json", - "image": "us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.7", + "id": "LegacyRStudio", + "label": "Legacy RStudio (R 4.2.3, Bioconductor 3.16, Python 3.10.6)", + "version": "3.16.1", + "updated": "2023-05-12", + "packages": "https://raw.githubusercontent.com/anvilproject/anvil-docker/master/anvil-rstudio-bioconductor/info/anvil-rstudio-bioconductor-3.16.1-versions.json", + "image": "us.gcr.io/broad-dsp-gcr-public/anvil-rstudio-bioconductor:3.16.1", "requiresSpark": false, - "isCommunity": true + "isRStudio": true }] diff --git a/config/conf.json b/config/conf.json index b1b452df..1e46c8fe 100644 --- a/config/conf.json +++ b/config/conf.json @@ -11,7 +11,7 @@ "gcr_image_repo" : "us.gcr.io/broad-dsp-gcr-public", "doc_bucket" : "gs://terra-docker-image-documentation", "doc_bucket_no_prefix" : "terra-docker-image-documentation", - "spark_version" : "2.4.5", + "spark_version" : "3.3.0", "image_data" : [ { "name" : "terra-jupyter-bioconductor", @@ -26,7 +26,7 @@ "tidyverse" ] }, - "version" : "2.1.6", + "version" : "2.2.1", "automated_flags" : { "generate_docs" : true, "include_in_ui" : true, @@ -47,7 +47,7 @@ "hail" ] }, - "version" : "1.0.19", + "version" : "1.1.1", "automated_flags" : { "generate_docs" : true, "include_in_ui" : true, @@ -68,7 +68,7 @@ "scikit-learn" ] }, - "version" : "1.0.13", + "version" : "1.1.1", "automated_flags" : { "generate_docs" : true, "include_in_ui" : true, @@ -83,8 +83,10 @@ "tools" : [ "python" ], - "packages" : {}, - "version" : "1.0.11", + "packages" : { + + }, + "version" : "1.1.1", "automated_flags" : { "generate_docs" : true, "include_in_ui" : false, @@ -99,8 +101,10 @@ "tools" : [ "r" ], - "packages" : {}, - "version" : "2.1.5", + "packages" : { + + }, + "version" : "2.2.1", "automated_flags" : { "include_in_ui" : false, "generate_docs" : true, @@ -117,8 +121,10 @@ "python", "r" ], - "packages" : {}, - "version" : "2.2.7", + "packages" : { + + }, + "version" : "2.3.1", "automated_flags" : { "include_in_ui" : true, "generate_docs" : true, @@ -134,8 +140,10 @@ "python", "r" ], - "packages" : {}, - "version" : "2.1.8", + "packages" : { + + }, + "version" : "2.2.1", "automated_flags" : { "include_in_ui" : false, "generate_docs" : false, @@ -145,22 +153,39 @@ } }, { - "name" : "terra-jupyter-gatk-ovtf", - "base_label" : "OpenVINO", + "name" : "terra-rstudio-aou", + "base_label" : "All of Us", "tools" : [ - "gatk", - "python", "r" ], - "packages" : {}, - "version" : "0.2.5", + "packages" : { + + }, + "version" : "0.1.1", "automated_flags" : { "include_in_ui" : false, - "generate_docs" : true, + "generate_docs" : false, "build" : true, "include_in_custom_dataproc" : false, "include_in_custom_gce" : false } + }, + { + "name" : "wondershaper", + "base_label" : "wondershaper", + "tools" : [ + ], + "packages" : { + + }, + "version" : "0.0.1", + "automated_flags" : { + "include_in_ui" : false, + "generate_docs" : false, + "build" : false, + "include_in_custom_dataproc" : false, + "include_in_custom_gce" : false + } } ] -} +} \ No newline at end of file diff --git a/config/legacy_static_images.json b/config/legacy_static_images.json index 2327397b..3c67ac55 100644 --- a/config/legacy_static_images.json +++ b/config/legacy_static_images.json @@ -1,11 +1,20 @@ [ { - "updated": "2021-10-07", - "image": "us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.0.2", - "label": "Legacy R / Bioconductor (R 4.1.1, Bioconductor 3.13, Python 3.7.10)", - "version": "2.0.2", + "updated": "2023-06-23", + "image": "us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.10", + "label": "Legacy R / Bioconductor (R 4.2.2, Bioconductor 3.16, Python 3.7.12)", + "version": "2.1.10", "requiresSpark": false, - "packages": "https://storage.googleapis.com/terra-docker-image-documentation/terra-jupyter-bioconductor-2.0.2-versions.json", + "packages": "https://storage.googleapis.com/terra-docker-image-documentation/terra-jupyter-bioconductor-2.1.10-versions.json", "id": "terra-jupyter-bioconductor_legacy" + }, + { + "updated": "2023-06-23", + "image": "us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk:2.2.14", + "label": "Legacy GATK (GATK 4.2.4.0, Python 3.7.12, R 4.3.0)", + "version": "2.2.14", + "requiresSpark": false, + "packages": "https://storage.googleapis.com/terra-docker-image-documentation/terra-jupyter-gatk:2.2.14-versions.json", + "id": "terra-jupyter-gatk_legacy" } ] diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile index 82f34db0..f9b8d216 100644 --- a/jenkins/Jenkinsfile +++ b/jenkins/Jenkinsfile @@ -229,7 +229,7 @@ pipeline { description: "Specify this, along with the images below, to force the build of specific images. Can be used in conjunction with the branch parameter for testing. Note this will fail if an image for the version in the VERSION file exists in google.") string(name: "BRANCH", defaultValue: "master", description: "Specify along with useCustomImageIdentifier to build the image with a specific name") - text(name: 'IMAGES_TO_BUILD', defaultValue: 'terra-jupyter-base\nterra-jupyter-python\nterra-jupyter-r\nterra-jupyter-gatk\nterra-jupyter-bioconductor\nterra-jupyter-hail\nterra-jupyter-aou\nterra-jupyter-gatk-ovtf', + text(name: 'IMAGES_TO_BUILD', defaultValue: 'terra-jupyter-base\nterra-jupyter-python\nterra-jupyter-r\nterra-jupyter-gatk\nterra-jupyter-bioconductor\nterra-jupyter-hail\nterra-jupyter-aou\nterra-rstudio-aou', description: 'The is a text field for newline separate images to build. Each image should be specified via the directory it resides in. The default value is a working example.') } diff --git a/scripts/generate_package_docs.py b/scripts/generate_package_docs.py index 28a9fd20..921757e5 100755 --- a/scripts/generate_package_docs.py +++ b/scripts/generate_package_docs.py @@ -21,7 +21,7 @@ def get_docs(params): docs = {} for tool in tools: - print "Writing doc for tool {}".format(tool) + print ("Writing doc for tool {}".format(tool)) #flattens the arrays of objects with packages:version key value pairs into single object with all key value pairs docs[tool] = utils.flatten_list_of_dicts(doc_builder[tool](params)) @@ -48,7 +48,7 @@ def get_r_doc(params): except AttributeError as e: raise ValueError("the R has produced output with an improperly formatted version. Something has likely gone wrong with this script, ensure that the output has a version properly parsed by the regex in the function write_r_doc.\n\tOutput: " + r_version_output) - bioconductor_version_output = utils.docker_exec(image_dir, "R --vanilla -s -e 'cat(as.character(BiocManager::version()))'") + bioconductor_version_output = utils.docker_exec(image_dir, "sh -c \"echo 'library(BiocManager) \n BiocManager::version()' > temp2.r; Rscript temp2.r; rm temp2.r\"") try: bioconductor_version = re.search("(\d+\.)(\d+)", bioconductor_version_output).group(0) except AttributeError as e: @@ -68,15 +68,18 @@ def get_r_doc(params): def get_python_doc(params): image_dir = params.image_dir python_version_output = utils.docker_exec(image_dir, "python3 --version") - raw_python_doc = utils.docker_exec(image_dir, "pip list --format=json") + raw_python_doc = utils.docker_exec(image_dir, "pip list --format=json --disable-pip-version-check") try: python_version = re.search("(\d+\.)(\d+\.)(\*|\d+)", python_version_output).group(0) except AttributeError as e: raise ValueError("the python command has produced output with an improperly formatted version. Something has likely gone wrong with this script, ensure that the output has a version properly parsed by the regex in the function write_python_doc. \n\tOutput: " + python_version_output) + json_python_doc = json.loads(raw_python_doc) + json_python_doc = list(map(lambda package: { package["name"]: package["version"] }, json_python_doc)) + json_python_doc.append({"python": python_version}) return json_python_doc diff --git a/terra-jupyter-aou/CHANGELOG.md b/terra-jupyter-aou/CHANGELOG.md index 4b7c1863..1403bd36 100644 --- a/terra-jupyter-aou/CHANGELOG.md +++ b/terra-jupyter-aou/CHANGELOG.md @@ -1,3 +1,121 @@ +## 2.2.1 - 2023-07-28 + +- Fix bug introduced in https://github.com/DataBiosphere/terra-docker/commit/4a5b4c9212aedcafa2f41fbeb2b161089341c578 +- Remove outdated cromshell alias +- Remove Java 8 and update `hail` to `0.2.120` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-120 for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.2.1` + +## 2.2.0 - 2023-06-23 + +- Update python from 3.7 to 3.10 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.2.0` + +## 2.1.22 - 2023-06-01T17:49:47.721962740Z + +- Update `terra-jupyter-r` to `2.1.10` + - Bioconductor 3.17 release with R 4.3 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.22` + +## 2.1.21 - 2023-05-24 +- Rollback `hail` to `0.2.107` +- Fix `cromshell` alias typo + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.21` +- + +## 2.1.20 - 2023-04-11 +- Update `hail` to `0.2.113` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-113) for details +- Replace r-saige with SAIGEgds. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.20` +- + +## 2.1.19 - 2023-03-16 +- Update `hail` to `0.2.112` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-112) for details +- Add alias for `cromshell` +- Remove `workspace_cromwell.p` +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.19` + +## 2.1.18 - 2023-03-13T17:26:34.247154Z + +- Update `terra-jupyter-base` to `1.0.14` + - Include `jupyter lab build` step + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.18` + +## 2.1.17 - 2023-02-08 +- Update `hail` to `0.2.109` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-109) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.17` + +## 2.1.16 - 2023-02-09T14:18:49.188741Z + +- Update `terra-jupyter-base` to `1.0.12` + - Add cloud.google.com gpg key. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.16` + +## 2.1.15 - 2022-12-14 +- Update `hail` to `0.2.107` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-107) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.15` + +## 2.1.14 - 2022-12-12 + +- Add tools for long reads support + - jellyfish 2.3.0 + - pangenie + - minimap2 2.24 + - whatshap + - TRF + - HMMER 3.3 + - rmblast 2.11.0 + - RepeatMasker 4.1.4 + - Text::Soundex + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.14` + +## 2.1.13 - 2022-11-22T20:49:44.192223144Z + +- Update `terra-jupyter-r` to `2.1.7` + - Update Bioconductor for 3.16 release + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.13` + +## 2.1.12 - 2022-10-21 +- Update `hail` to `0.2.104` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-104) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.12` + +## 2.1.11 - 2022-10-04 +- Update `hail` to `0.2.101` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-101) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.11` + +## 2.1.10 - 2022-09-23 +- Update `hail` to `0.2.100` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-100) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.10` + +## 2.1.9 - 2022-09-02 + + - Add support for QIIME2 + - Update `hail` to `0.2.98` + - See https://hail.is/docs/0.2/change_log.html#version-0-2-98) for details + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-aou:2.1.9` + ## 2.1.8 - 2022-07-12 - Revert attempted Java11 upgrade back to Java8 diff --git a/terra-jupyter-aou/Dockerfile b/terra-jupyter-aou/Dockerfile index a492307f..409606fa 100644 --- a/terra-jupyter-aou/Dockerfile +++ b/terra-jupyter-aou/Dockerfile @@ -1,4 +1,4 @@ -FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk:2.2.7 +FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk:2.3.1 USER root @@ -23,14 +23,12 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* + ENV PIP_USER=false -# pandas-profiling 3.1.0 causes conflicts with pyplot -# pandas-profiling 3.2.0 is incompatible with markupsafe 2.0.1 -# 3.0.0 (lower is untested) avoids both of these issues. RUN pip3 install --upgrade \ - "pandas_profiling<=3.0.0" \ - "markupsafe==2.0.1" + "pandas_profiling==3.6.6" \ + "markupsafe==2.1.2" RUN pip3 install \ nbstripout \ @@ -38,6 +36,7 @@ RUN pip3 install \ dsub \ "git+https://github.com/all-of-us/workbench-snippets.git#egg=terra_widgets&subdirectory=py" + # 2.0.0 has breaking changes and we're not ready for them yet. RUN pip3 install "igv-jupyter>=1.0.0,<2.0.0" RUN jupyter nbextension enable --py igv --sys-prefix @@ -47,7 +46,8 @@ RUN jupyter nbextension enable --py igv --sys-prefix ENV PYTHONPATH $PYTHONPATH:/usr/lib/spark/python ENV PYSPARK_PYTHON=python3 -ENV HAIL_VERSION=0.2.96 + +ENV HAIL_VERSION=0.2.120 RUN find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel /opt/conda/share/jupyter/kernels \ @@ -58,11 +58,11 @@ RUN find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ && update-alternatives --install /etc/hadoop/conf hadoop-conf /etc/hadoop/conf.empty 100 \ && update-alternatives --install /etc/hive/conf hive-conf /etc/hive/conf.dist 100 \ && apt-get update \ - && apt install -yq --no-install-recommends \ - g++ \ - liblz4-dev \ - # specify Java 8 - && update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java \ + # Note that starting on 2.1.x, dataproc VM also provides java 11, which is incompatible with 8. No need to install it in the container + # && apt install -yq --no-install-recommends openjdk-8-jdk \ + # g++ \ + # liblz4-dev \ + # && update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java \ && pip3 install pypandoc gnomad \ && pip3 install --no-dependencies hail==$HAIL_VERSION \ && X=$(mktemp -d) \ @@ -87,7 +87,7 @@ RUN cd /usr/local/share && \ ENV PLINK_VERSION 20210416 RUN mkdir -p /tmp/plink && \ cd /tmp/plink && \ - curl -L -o plink.zip "http://s3.amazonaws.com/plink1-assets/plink_linux_x86_64_${PLINK_VERSION}.zip" && \ + curl -fsSL -o plink.zip "http://s3.amazonaws.com/plink1-assets/plink_linux_x86_64_${PLINK_VERSION}.zip" && \ unzip plink.zip && \ mv plink /bin/plink && \ cd $HOME && \ @@ -95,7 +95,7 @@ RUN mkdir -p /tmp/plink && \ RUN mkdir -p /tmp/plink2 && \ cd /tmp/plink2 && \ - curl -L -o plink2.zip "https://s3.amazonaws.com/plink2-assets/alpha2/plink2_linux_x86_64.zip" && \ + curl -fsSL -o plink2.zip "https://s3.amazonaws.com/plink2-assets/alpha2/plink2_linux_x86_64.zip" && \ unzip plink2.zip && \ mv plink2 /bin/plink2 && \ cd $HOME && \ @@ -103,7 +103,7 @@ RUN mkdir -p /tmp/plink2 && \ RUN mkdir -p /tmp/prsice && \ cd /tmp/prsice && \ - curl -L -o prsice.zip https://github.com/choishingwan/PRSice/releases/download/2.3.5/PRSice_linux.zip && \ + curl -fsSL -o prsice.zip https://github.com/choishingwan/PRSice/releases/download/2.3.5/PRSice_linux.zip && \ unzip prsice.zip && \ rm prsice.zip && \ cd $HOME && \ @@ -111,10 +111,10 @@ RUN mkdir -p /tmp/prsice && \ ln -s "/opt/prsice/PRsice_linux" /bin/prsice # BOLT-LMM install -ENV BOLT_LMM_VERSION v2.3.6 +ENV BOLT_LMM_VERSION v2.4.1 RUN mkdir -p /tmp/bolt-lmm && \ cd /tmp/bolt-lmm && \ - curl -L -o bolt-lmm.tar.gz "https://storage.googleapis.com/broad-alkesgroup-public/BOLT-LMM/downloads/BOLT-LMM_${BOLT_LMM_VERSION}.tar.gz" && \ + curl -fsSL -o bolt-lmm.tar.gz "https://storage.googleapis.com/broad-alkesgroup-public/BOLT-LMM/downloads/BOLT-LMM_${BOLT_LMM_VERSION}.tar.gz" && \ tar -xvzf bolt-lmm.tar.gz && \ mv "BOLT-LMM_${BOLT_LMM_VERSION}" /opt/bolt-lmm && \ ln -s "/opt/bolt-lmm/bolt" /bin/bolt && \ @@ -125,7 +125,7 @@ RUN mkdir -p /tmp/bolt-lmm && \ ENV REGENIE_VERSION v2.0.2 RUN mkdir -p /tmp/regenie && \ cd /tmp/regenie && \ - curl -L -o regenie.zip "https://github.com/rgcgithub/regenie/releases/download/${REGENIE_VERSION}/regenie_${REGENIE_VERSION}.gz_x86_64_Linux.zip" && \ + curl -fsSL -o regenie.zip "https://github.com/rgcgithub/regenie/releases/download/${REGENIE_VERSION}/regenie_${REGENIE_VERSION}.gz_x86_64_Linux.zip" && \ unzip regenie.zip && \ mv regenie_${REGENIE_VERSION}.gz_x86_64_Linux /bin/regenie && \ cd $HOME && \ @@ -152,7 +152,7 @@ RUN curl -SL --output dotnet.tar.gz "https://dotnetcli.azureedge.net/dotnet/Runt ENV GCTA_VERSION=v1.94.0Beta RUN mkdir -p /tmp/gcta && \ cd /tmp/gcta && \ - curl -L -o gcta.zip "https://yanglab.westlake.edu.cn/software/gcta/bin/gcta_${GCTA_VERSION}_linux_kernel_4_x86_64.zip" && \ + curl -fsSL -o gcta.zip "https://yanglab.westlake.edu.cn/software/gcta/bin/gcta_${GCTA_VERSION}_linux_kernel_4_x86_64.zip" && \ unzip gcta.zip && \ mv "gcta_${GCTA_VERSION}_linux_kernel_4_x86_64/gcta_${GCTA_VERSION}_linux_kernel_4_x86_64_static" /bin/gcta && \ cd $HOME && \ @@ -161,7 +161,7 @@ RUN mkdir -p /tmp/gcta && \ # Install bcftools RUN mkdir -p /tmp/bcftools && \ cd /tmp/bcftools && \ - curl -L -o bcftools.tar.bz2 https://github.com/samtools/bcftools/releases/download/1.12/bcftools-1.12.tar.bz2 && \ + curl -fsSL -o bcftools.tar.bz2 https://github.com/samtools/bcftools/releases/download/1.12/bcftools-1.12.tar.bz2 && \ bzip2 -d bcftools.tar.bz2 && \ tar xvf bcftools.tar && \ cd bcftools-1.12 && \ @@ -173,7 +173,7 @@ RUN mkdir -p /tmp/bcftools && \ # Install vcftools RUN mkdir -p /tmp/vcftools && \ cd /tmp/vcftools && \ - curl -L -o vcftools.tar.gz https://github.com/vcftools/vcftools/releases/download/v0.1.16/vcftools-0.1.16.tar.gz && \ + curl -fsSL -o vcftools.tar.gz https://github.com/vcftools/vcftools/releases/download/v0.1.16/vcftools-0.1.16.tar.gz && \ tar xvzf vcftools.tar.gz && \ cd vcftools-0.1.16 && \ ./configure && \ @@ -184,14 +184,89 @@ RUN mkdir -p /tmp/vcftools && \ RUN R -e 'BiocManager::install(c("GENESIS"))' -# TODO(IA-3261): Remove this hack, and just conda install normally. R SAIGE -# unfortunately is difficult to install outside of conda. Conda is also not -# fully supported on the terra-docker base image. Here we workaround this by -# installing (as root) via conda, and including the R lib path. Order is -# important here, as packages.install() will target the first path in this list, -# and the jupyter user will have readonly access to the conda path. -ENV R_LIBS /usr/local/lib/R/site-library:/opt/conda/lib/R/library -RUN conda install -c bioconda r-saige +RUN R -e 'BiocManager::install(c("SAIGEgds","SNPRelate"))' + +# Install hdf5 library and h5py +RUN apt-get update && \ + apt install -yq --no-install-recommends \ + libhdf5-serial-dev \ + cmake \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +RUN pip install --upgrade pip && pip install --verbose h5py + +# RW-8725 INSTALL QIMEE2 +RUN curl -fsSL -o qiime2-2022.8.yml https://data.qiime2.org/distro/core/qiime2-2022.8-py38-linux-conda.yml && \ + conda env create -n qiime2-2022.8 --file qiime2-2022.8.yml && \ + rm qiime2-2022.8.yml + +# Install jellyfish +RUN mkdir -p /tmp/jellyfish && \ + cd /tmp/jellyfish && \ + curl -fsSL -o jellyfish.tar.gz https://github.com/gmarcais/Jellyfish/releases/download/v2.3.0/jellyfish-2.3.0.tar.gz && \ + tar xvf jellyfish.tar.gz && \ + cd jellyfish-2.3.0 && \ + ./configure && \ + make && \ + make install && \ + cd $HOME && \ + rm -rf /tmp/jellyfish + +# Install pangenie +RUN mkdir -p /tmp/pangenie && \ + cd /tmp/pangenie && \ + git clone https://github.com/eblerjana/pangenie.git && \ + cd pangenie && \ + mkdir build; cd build; cmake .. ; make && cd src && \ + mv *.so /lib && \ + mv * /bin/ && \ + cd $HOME && \ + rm -rf /tmp/pangenie + +# Install minimap2 +RUN curl -fsSL https://github.com/lh3/minimap2/releases/download/v2.24/minimap2-2.24_x64-linux.tar.bz2 | tar -jxvf - && cd minimap2-2.24_x64-linux/ && cp minimap2 /bin/minimap2 && cd $HOME + +# Run whatshap (>1h install) +# RUN conda install -c bioconda whatshap +# Alternatively, not using conda (1 min install) +RUN pip install --verbose git+https://github.com/whatshap/whatshap + +# TRF +RUN mkdir -p /tmp/trf && cd /tmp/trf && \ + git clone https://github.com/Benson-Genomics-Lab/TRF.git && cd TRF && \ + mkdir build && cd build && ../configure && make && make install && \ + cd $HOME && \ + rm -rf /tmp/trf + +# HMMER +RUN mkdir -p /tmp/hmmer && cd /tmp/hmmer && \ + curl -fsSL -o hmmer-3.3.tar.gz http://eddylab.org/software/hmmer/hmmer-3.3.tar.gz && \ + tar -xzf hmmer-3.3.tar.gz && cd hmmer-3.3 && ./configure && \ + make && make install && cd .. && rm -r hmmer-3.3 hmmer-3.3.tar.gz && cd $HOME && \ + rm -rf /tmp/hmmer + +# rmblast (200M) +RUN mkdir -p /tmp/rmblast && cd /tmp/rmblast && \ + curl -fsSL -o rmblast-2.11.0+-x64-linux.tar.gz http://www.repeatmasker.org/rmblast-2.11.0+-x64-linux.tar.gz && \ + tar -zxvf rmblast-2.11.0+-x64-linux.tar.gz && \ + cp rmblast-2.11.0/bin/* /usr/local/bin && \ + cd $HOME && \ + rm -rf /tmp/rmblast + +RUN PERL_MM_USE_DEFAULT=1 cpan Text::Soundex + +# Install RepeatMasker (900M) +RUN curl -fsSL http://www.repeatmasker.org/RepeatMasker/RepeatMasker-4.1.4.tar.gz \ + | (cd /usr/local; tar -xz) + +# Remove workspace_cromwell script as it may allow user bypass AoU to create Cromwell via Leo directly +RUN rm -f /usr/local/bin/workspace_cromwell.py + +# Downgrade Jinja2 as the newer version breaks the notebook +# See https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-0 +RUN pip install --force-reinstall -v "Jinja2==3.0.3" + +ENV PATH="$PATH:/usr/local/RepeatMasker" ENV PIP_USER=true USER $USER diff --git a/terra-jupyter-aou/tests/aou_smoke_test.ipynb b/terra-jupyter-aou/tests/aou_smoke_test.ipynb index 6975fb39..1bb59ad5 100644 --- a/terra-jupyter-aou/tests/aou_smoke_test.ipynb +++ b/terra-jupyter-aou/tests/aou_smoke_test.ipynb @@ -149,7 +149,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.10" + "version": "3.10.9" }, "toc": { "base_numbering": 1, diff --git a/terra-jupyter-base/CHANGELOG.md b/terra-jupyter-base/CHANGELOG.md index 6c43955d..41ed5129 100644 --- a/terra-jupyter-base/CHANGELOG.md +++ b/terra-jupyter-base/CHANGELOG.md @@ -1,3 +1,28 @@ +## 1.1.1 - 2023-07-06 + +- Fix bug introduced in https://github.com/DataBiosphere/terra-docker/commit/4a5b4c9212aedcafa2f41fbeb2b161089341c578 + +## 1.1.0 - 2023-06-23 + +- Upgrade python 3.7 to 3.10 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:1.1.0` +## 1.0.14 - 2023-03-13T17:26:34.128391Z + +- Include `jupyter lab build` step + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:1.0.14` + +## 1.0.13 - 2023-03-09 + +- Include `jupyter lab build` step + +## 1.0.12 - 2023-02-09T14:18:48.948718Z + +- Add cloud.google.com gpg key. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:1.0.12` + ## 1.0.11 - 2022-06-23T10:58:12.961300Z - Fix leo_url variable in workspace_cromwell.py script for AoU projects diff --git a/terra-jupyter-base/Dockerfile b/terra-jupyter-base/Dockerfile index af99d42e..95d95f54 100644 --- a/terra-jupyter-base/Dockerfile +++ b/terra-jupyter-base/Dockerfile @@ -1,6 +1,6 @@ # adapted from https://hub.docker.com/r/jupyter/base-notebook/ AKA https://github.com/jupyter/docker-stacks/tree/master/base-notebook -FROM gcr.io/deeplearning-platform-release/tf-gpu.2-7 +FROM gcr.io/deeplearning-platform-release/tf-gpu.2-11.py310 USER root @@ -21,11 +21,13 @@ RUN sudo -i \ # see article: https://developer.nvidia.com/blog/updating-the-cuda-linux-gpg-repository-key/ # first we remove the bad keys from base image (https://github.com/NVIDIA/nvidia-docker/issues/1631#issuecomment-1112828208) RUN rm /etc/apt/sources.list.d/cuda.list \ - && rm /etc/apt/sources.list.d/nvidia-ml.list \ && sudo apt-key del 7fa2af80 \ && curl -L -O https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb \ && sudo dpkg -i cuda-keyring_1.0-1_all.deb +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - + RUN apt-get update && apt-get install -yq --no-install-recommends \ # gnupg requirement dirmngr \ @@ -45,6 +47,8 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ + libexempi3 \ + libv8-3.14-dev \ llvm \ libncurses5-dev \ libncursesw5-dev \ @@ -52,8 +56,6 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ libffi-dev \ liblzma-dev \ python-openssl \ - libexempi3 \ - libv8-3.14-dev \ # install script requirements sudo \ locales \ @@ -62,12 +64,12 @@ RUN apt-get update && apt-get install -yq --no-install-recommends \ # openjdk 11 default-jre \ default-jdk \ - # Uncomment en_US.UTF-8 for inclusion in generation - && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ - # Generate locale - && locale-gen \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + # Uncomment en_US.UTF-8 for inclusion in generation + && sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen \ + # Generate locale + && locale-gen \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* ENV LC_ALL en_US.UTF-8 @@ -92,31 +94,29 @@ ENV JUPYTER_PORT 8000 ENV JUPYTER_HOME /etc/jupyter RUN pip3 -V \ - # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. - # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) - # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this - && pip3 install python-datauri \ - && pip3 install jupyter_contrib_nbextensions \ - && pip3 install jupyter_nbextensions_configurator \ - && pip3 install markupsafe==2.0.1 \ - # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624 - && pip3 install "nbconvert>=6.4.5" \ - # for jupyter_delocalize.py and jupyter_notebook_config.py - && pip3 install requests \ - && pip3 install firecloud \ - && pip3 install terra-notebook-utils \ - && pip3 install crcmod \ - # For gcloud alpha storage support. - && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party - -# install Cromshell 2.0 -RUN git clone https://github.com/broadinstitute/cromshell.git \ - && cd cromshell \ - && git checkout cromshell_2.0 \ - && pip install . - -# verify cromshell 2.0 is installed -RUN cromshell-alpha version + # When we upgraded from jupyter 5.7.8 to 6.1.1, we broke terminal button on terra-ui. + # Hence, make sure to manually test out "launch terminal" button (the button in the green bar next to start and stop buttons) + # to make sure we don't accidentally break it every time we upgrade notebook version until we figure out an automation test for this + && pip3 install python-datauri \ + && pip3 install jupyter_contrib_nbextensions \ + && pip3 install jupyter_nbextensions_configurator \ + && pip3 install markupsafe==2.1.2 \ + # Avoid broken lower versions: https://github.com/jupyter/nbconvert/pull/1624, pinning to latest known working version on terra + && pip3 install "nbconvert==7.2.9" \ + # Avoid notebook extension issues + && pip3 install "nbclassic<0.5" \ + # for jupyter_delocalize.py and jupyter_notebook_config.py + && pip3 install requests \ + && pip3 install firecloud \ + && pip3 install terra-notebook-utils \ + && pip3 install crcmod \ + # For gcloud alpha storage support. + && pip3 install google-crc32c --target /usr/lib/google-cloud-sdk/lib/third_party \ + && pip3 install cromshell==2.0.0 + +# to allow file-saving in different formats +# toc2 is not compatible with versions of notebooks over 5.x - deprecated +# RUN jupyter nbextension enable toc2/main # tmp hack min-5 # I'm not installing jupyterlab and I can't update init-actions.sh to not access it @@ -140,14 +140,18 @@ COPY jupyter_notebook_config.py $JUPYTER_HOME # copy workspace_cromwell.py script and make it runnable by all users RUN curl -o /usr/local/bin/workspace_cromwell.py https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ - && chmod +x /usr/local/bin/workspace_cromwell.py + && chmod +x /usr/local/bin/workspace_cromwell.py RUN chown -R $USER:users $JUPYTER_HOME \ -# Disable nb_conda for now. Consider re-enable in the future - && jupyter nbextension disable nb_conda --py --sys-prefix \ - && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ -# You can get kernel directory by running `jupyter kernelspec list` - && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel /opt/conda/share/jupyter/kernels + # Disable nb_conda for now. Consider re-enable in the future + && jupyter nbextension disable nb_conda --py --sys-prefix \ + && find $JUPYTER_HOME/scripts -name '*.sh' -type f | xargs chmod +x \ + # You can get kernel directory by running `jupyter kernelspec list` + && $JUPYTER_HOME/scripts/kernel/kernelspec.sh $JUPYTER_HOME/scripts/kernel /opt/conda/share/jupyter/kernels + +# Run a `jupyter lab build` during the Docker image build step so users don't have to do it on their own +# The webpack build during `jupyter lab build` is memory intensive so give it access to more memory as well +RUN NODE_OPTIONS=--max-old-space-size=4096 jupyter lab build --minimize=True USER $USER EXPOSE $JUPYTER_PORT diff --git a/terra-jupyter-base/README.md b/terra-jupyter-base/README.md index d1cf36ad..446559e1 100644 --- a/terra-jupyter-base/README.md +++ b/terra-jupyter-base/README.md @@ -8,7 +8,7 @@ The terra-jupyter-base extends the [Ubuntu base image]("https://github.com/tiano - OS prerequisites - google-cloud-sdk -- Python 3.7 +- Python 3.10 - pip - conda - Jupyter & JupyterLab diff --git a/terra-jupyter-base/tests/smoke_test.py b/terra-jupyter-base/tests/smoke_test.py index a2191a5a..f1461cf7 100644 --- a/terra-jupyter-base/tests/smoke_test.py +++ b/terra-jupyter-base/tests/smoke_test.py @@ -19,6 +19,7 @@ def test_terra_notebook_utils(): import terra_notebook_utils # Re-add this test if one day base image no longer declares `/home/jupyter` as a VOLUME +# toc2 is also not supporting our version of notebook anymore # def test_extensions(): # output = subprocess.check_output("jupyter nbextension list", shell=True) # if not "toc2/main" in str(output): diff --git a/terra-jupyter-bioconductor/CHANGELOG.md b/terra-jupyter-bioconductor/CHANGELOG.md index e4ee4715..164dc563 100644 --- a/terra-jupyter-bioconductor/CHANGELOG.md +++ b/terra-jupyter-bioconductor/CHANGELOG.md @@ -1,3 +1,51 @@ +## 2.2.1 - 2023-07-06 + +- Fix bug introduced in https://github.com/DataBiosphere/terra-docker/commit/4a5b4c9212aedcafa2f41fbeb2b161089341c578 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.2.1` + +## 2.2.0 - 2023-06-23 + +- Update python 3.7 to 3.10 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.2.0` + +## 2.1.11 - 2023-06-01T17:49:47.622531112Z + +- Update `terra-jupyter-r` to `2.1.10` + - Bioconductor 3.17 release with R 4.3 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.11` + +## 2.1.10 - 2023-03-13T17:26:34.148443Z + +- Update `terra-jupyter-base` to `1.0.14` + - Include `jupyter lab build` step + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.10` + +## 2.1.9 - 2023-02-09T14:18:48.987542Z + +- Update `terra-jupyter-base` to `1.0.12` + - Add cloud.google.com gpg key. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.9` + +## 2.1.8 - 2022-11-22T20:49:44.056023723Z + +- Update `terra-jupyter-r` to `2.1.7` + - Update Bioconductor for 3.16 release + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.8` + +## 2.1.7 - 2022-07-27T13:32:19.884050Z + +- Moved AnVIL and supporting packages to be installed in parent image. +- Update `terra-jupyter-r` to `2.1.6` + - Installs AnVIL and supporting packages. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-bioconductor:2.1.7` + ## 2.1.6 - 2022-07-22 - Fix anvil package install diff --git a/terra-jupyter-bioconductor/Dockerfile b/terra-jupyter-bioconductor/Dockerfile index 5118e483..019c1c43 100644 --- a/terra-jupyter-bioconductor/Dockerfile +++ b/terra-jupyter-bioconductor/Dockerfile @@ -1,10 +1 @@ -FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.5 - -USER root - -## Install Bioconductor Core packages -RUN curl -O https://raw.githubusercontent.com/anvilproject/anvil-docker/master/anvil-rstudio-bioconductor/install.R \ - && R -f install.R \ - && rm -rf install.R - -USER $USER +FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.2.1 \ No newline at end of file diff --git a/terra-jupyter-bioconductor/tests/smoke_test.ipynb b/terra-jupyter-bioconductor/tests/smoke_test.ipynb index f346c97c..1632891a 100644 --- a/terra-jupyter-bioconductor/tests/smoke_test.ipynb +++ b/terra-jupyter-bioconductor/tests/smoke_test.ipynb @@ -1,6 +1,7 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", "id": "3d33c4ec", "metadata": {}, @@ -12,7 +13,11 @@ "cell_type": "code", "execution_count": null, "id": "52ca0aa1", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('assertthat')" @@ -22,7 +27,11 @@ "cell_type": "code", "execution_count": null, "id": "martial-fitness", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "require(assertthat)" @@ -32,7 +41,11 @@ "cell_type": "code", "execution_count": null, "id": "blessed-burden", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::version()" @@ -42,7 +55,11 @@ "cell_type": "code", "execution_count": null, "id": "7cde5e74", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install(\"rsbml\")" @@ -52,7 +69,11 @@ "cell_type": "code", "execution_count": null, "id": "fc54067c", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(rsbml)" @@ -62,7 +83,11 @@ "cell_type": "code", "execution_count": null, "id": "699ca08a", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install(\"RCurl\")" @@ -72,7 +97,11 @@ "cell_type": "code", "execution_count": null, "id": "91f1d7ad", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(RCurl)" @@ -82,7 +111,11 @@ "cell_type": "code", "execution_count": null, "id": "b941edc1", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(\"SingleCellExperiment\")" @@ -92,7 +125,11 @@ "cell_type": "code", "execution_count": null, "id": "3adf1f30", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "# This will be fixed in bioc (R 4.0)\n", @@ -103,7 +140,11 @@ "cell_type": "code", "execution_count": null, "id": "bibliographic-intent", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "# assert_that('DESeq2' %in% rownames(installed.packages()) == 'TRUE')" @@ -113,7 +154,11 @@ "cell_type": "code", "execution_count": null, "id": "3663e1de", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(\"ShortRead\")" @@ -123,7 +168,11 @@ "cell_type": "code", "execution_count": null, "id": "7c2881aa", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(\"GenomicAlignments\")" @@ -133,7 +182,11 @@ "cell_type": "code", "execution_count": null, "id": "15a5ba29", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(\"GenomicFeatures\")" @@ -143,7 +196,11 @@ "cell_type": "code", "execution_count": null, "id": "45051b29", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "find.package('ShortRead')" @@ -153,7 +210,11 @@ "cell_type": "code", "execution_count": null, "id": "09a7477d", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('XML')" @@ -163,7 +224,11 @@ "cell_type": "code", "execution_count": null, "id": "c33a766b", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(XML)" @@ -173,7 +238,11 @@ "cell_type": "code", "execution_count": null, "id": "c90bd714", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('Rgraphviz')" @@ -183,7 +252,11 @@ "cell_type": "code", "execution_count": null, "id": "0ba20610", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('BiocSklearn')" @@ -193,7 +266,11 @@ "cell_type": "code", "execution_count": null, "id": "8cd7e307", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library('BiocSklearn')" @@ -203,27 +280,41 @@ "cell_type": "code", "execution_count": null, "id": "fbb3f21b", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ - "BiocManager::install('rhdf5')" + "# Currently times out on CI but works locally\n", + "# BiocManager::install('rhdf5')" ] }, { "cell_type": "code", "execution_count": null, "id": "919ab35c", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ - "library(rhdf5)" + "# Currently times out on CI but works locally\n", + "# library(rhdf5)" ] }, { "cell_type": "code", "execution_count": null, "id": "a7e397b2", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "# Currently broken\n", @@ -234,7 +325,11 @@ "cell_type": "code", "execution_count": null, "id": "c3d238c6", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "# library(ChemmineOB)" @@ -244,7 +339,11 @@ "cell_type": "code", "execution_count": null, "id": "4dad1ef3", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('EBImage')" @@ -254,7 +353,11 @@ "cell_type": "code", "execution_count": null, "id": "4749b448", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(EBImage)" @@ -264,7 +367,11 @@ "cell_type": "code", "execution_count": null, "id": "fdb05268", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('RMySQL')" @@ -274,7 +381,11 @@ "cell_type": "code", "execution_count": null, "id": "aa8c5778", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(RMySQL)" @@ -284,7 +395,11 @@ "cell_type": "code", "execution_count": null, "id": "592faa65", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('DirichletMultinomial')" @@ -294,7 +409,11 @@ "cell_type": "code", "execution_count": null, "id": "e87ffece", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(DirichletMultinomial)" @@ -304,7 +423,11 @@ "cell_type": "code", "execution_count": null, "id": "f3e13a29", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('rjags')" @@ -314,7 +437,11 @@ "cell_type": "code", "execution_count": null, "id": "72a7c4d4", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library('rjags')" @@ -324,7 +451,11 @@ "cell_type": "code", "execution_count": null, "id": "invisible-romantic", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "BiocManager::install('testit')" @@ -334,7 +465,11 @@ "cell_type": "code", "execution_count": null, "id": "a3c864c5", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "# Comment out for now\n", @@ -345,7 +480,11 @@ "cell_type": "code", "execution_count": null, "id": "6a4b7b40", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "library(protolite)" @@ -356,7 +495,10 @@ "execution_count": null, "id": "younger-greenhouse", "metadata": { - "scrolled": true + "scrolled": true, + "vscode": { + "languageId": "r" + } }, "outputs": [], "source": [ @@ -367,7 +509,11 @@ "cell_type": "code", "execution_count": null, "id": "approximate-cutting", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "assert_that(\"SingleCellExperiment\" %in% installed.packages() == 'TRUE')" @@ -377,7 +523,11 @@ "cell_type": "code", "execution_count": null, "id": "special-fishing", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "assert_that(\"GenomicAlignments\" %in% installed.packages() == 'TRUE')" @@ -387,7 +537,11 @@ "cell_type": "code", "execution_count": null, "id": "conscious-hacker", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "assert_that(\"ShortRead\" %in% installed.packages() == 'TRUE')" @@ -397,7 +551,11 @@ "cell_type": "code", "execution_count": null, "id": "6f3c92ba", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "r" + } + }, "outputs": [], "source": [ "assert_that(\"Seurat\" %in% installed.packages() == 'TRUE')" diff --git a/terra-jupyter-gatk-ovtf/CHANGELOG.md b/terra-jupyter-gatk-ovtf/CHANGELOG.md deleted file mode 100644 index 3d194808..00000000 --- a/terra-jupyter-gatk-ovtf/CHANGELOG.md +++ /dev/null @@ -1,107 +0,0 @@ -## 0.2.5 - 2022-06-23T10:58:12.961300Z - -- Update `terra-jupyter-base` to `1.0.10` - - Fix leo_url variable in workspace_cromwell.py script for AoU projects - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.2.4` - -## 0.2.4 - 2022-06-23T10:58:12.961300Z - -- reverted, do not use - -## 0.2.3 - 2022-05-20T18:06:39.600661Z - -- Update `terra-jupyter-base` to `1.0.9` - - Fix adding workspace_cromwell.py script to manage Cromwell App - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.2.3` - -## 0.2.2 - 2022-05-17T17:14:41.382147Z - -- Update `terra-jupyter-base` to `1.0.8` - - Add script that manages Cromwell app - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.2.2` - -## 0.2.1 - 2022-05-10T22:08:26.070940Z - -- Update `terra-jupyter-base` to `1.0.7` - - Install Cromshell 2.0 (https://github.com/broadinstitute/cromshell) - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.2.1` - -## 0.2.0 - 2022-05-02 - -- Update `terra-jupyter-r` to `2.1.0` - - Update Bioconductor to 3.15.0 and R to 4.2.0 - -## 0.1.10 - 2022-05-02T14:33:24.040339Z - -- Update `terra-jupyter-base` to `1.0.6` - - use new nvidia key - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.10` - -## 0.1.9 - 2022-04-17 - -- Update `terra-jupyter-base` to `1.0.5` - - Install gcloud alpha storage dependency - -## 0.1.8 - 2022-02-02T20:47:42.409160Z - -- Update `terra-jupyter-base` to `1.0.4` - - update notebook_dir to $HOME; install scikit-learn-intelex, xgboost - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.8` - -## 0.1.7 - 2022-01-26T17:05:04.719540Z - -- OVTF1.1.0 released, Updated to GATK 4.2.4.1, TF2.7, GPU support enabled - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.7` - -## 0.1.6 - 2022-01-06T18:21:41.248206Z - -- Update `terra-jupyter-base` to `1.0.3` - - Bumping Google Deeplearning image to 2.7, removing gcc-6 and gcc-8 install due to package problems - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.6` - -## 0.1.5 - 2021-12-14T19:48:03.867076Z - -- Update `terra-jupyter-r` to `2.0.3` - - Update Terra Jupyter image to use latest Bioconductor 3.14 - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.5` - -## 0.1.4 - 2021-12-15T18:30:57.568303Z - -- Upgrade gatk image to 4.2.4.0 - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.4` - -## 0.1.3 - 2021-10-07T14:47:43.454585Z - -- Update `terra-jupyter-base` to `1.0.2` - - Update AsyncMappingKernelManager https://github.com/jupyter/notebook/issues/6164 -- Unpinning cwltool version and updating protobuf version to 3.18 - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.3` - -## 0.1.2 - 2021-09-22T15:25:34.080267Z - -- Intel OVTF patch for updating to TF2.6.0/2.5.0 - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.2` - -## 0.1.1 - 2021-09-10T15:10:44.164165Z - -- Update `terra-jupyter-base` to `1.0.1` - - Update base image to gcr.io/deeplearning-platform-release/tf2-gpu.2-6 to support TensorFlow 2.6.0 - - Fix multipart Jupyter uploads - -Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:0.1.1` - -## 0.1.0 - 08/25/2021 - -- Added OpenVINO integration with TensorFlow (OVTF) diff --git a/terra-jupyter-gatk-ovtf/Dockerfile b/terra-jupyter-gatk-ovtf/Dockerfile deleted file mode 100644 index 8d98db14..00000000 --- a/terra-jupyter-gatk-ovtf/Dockerfile +++ /dev/null @@ -1,70 +0,0 @@ -FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-python:1.0.11 AS python - -FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.5 - -# copy everything pip installed from the python image -COPY --from=python /opt/conda/lib/python3.7/site-packages /opt/conda/lib/python3.7/site-packages - -USER root - -# need to apt-get everything for python since we can only copy pip installed packages -RUN apt-get update && apt-get install -yq --no-install-recommends \ - python3.7-dev \ - python-tk \ - openjdk-8-jdk \ - tk-dev \ - libssl-dev \ - xz-utils \ - libhdf5-dev \ - openssl \ - make \ - liblzo2-dev \ - zlib1g-dev \ - libz-dev \ - samtools \ - git-lfs \ - # specify Java 8 - && update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install GATK -ENV GATK_VERSION 4.2.4.1 -ENV GATK_ZIP_PATH /tmp/gatk-${GATK_VERSION}.zip - -RUN curl -L -o $GATK_ZIP_PATH https://github.com/broadinstitute/gatk/releases/download/$GATK_VERSION/gatk-$GATK_VERSION.zip \ - && unzip -o $GATK_ZIP_PATH -d /etc/ \ - && ln -s /etc/gatk-$GATK_VERSION/gatk /bin/gatk - -# Install OpenVINOâ„¢ integration with TensorFlow (OVTF) -ENV TF_VER 2.7.0 -ENV KERAS_VER 2.7.0 -ENV OVTF_VER 1.1.0 -ENV TF_ENABLE_ONEDNN_OPTS 1 - -ENV PIP_USER=false - -# Force reinstalling TF and Keras to satisfy OVTF compatibility. -RUN pip install --force-reinstall tensorflow==$TF_VER keras==$KERAS_VER -RUN conda install six typing_extensions --force-reinstall \ - && pip install openvino_tensorflow==$OVTF_VER \ - && pip install matplotlib sklearn \ - && pip install /etc/gatk-$GATK_VERSION/gatkPythonPackageArchive.zip -ENV PIP_USER=true - -COPY gatk-ovtf.patch /etc/gatk-$GATK_VERSION/gatk-ovtf.patch -COPY ovtf-cuda-disabling.patch /etc/gatk-$GATK_VERSION/ovtf-cuda-disabling.patch - -# Apply patch to GATK to fix TFv2 compatibility. -RUN cd /opt/conda/lib/python3.7/site-packages/vqsr_cnn/vqsr_cnn/ \ - && patch -p1 < /etc/gatk-$GATK_VERSION/gatk-ovtf.patch - -# Apply patch to OVTF to disable CUDA devices if OVTF is enabled. -RUN cd /opt/conda/lib/python3.7/site-packages/openvino_tensorflow/ \ - && patch __init__.py < /etc/gatk-$GATK_VERSION/ovtf-cuda-disabling.patch - -COPY --chown=jupyter:users run_gatk.sh /home/jupyter/run_gatk.sh -COPY --chown=jupyter:users GATK-OVTF-Notebook.ipynb /home/jupyter/GATK-OVTF-Notebook.ipynb - -ENV USER jupyter -USER $USER diff --git a/terra-jupyter-gatk-ovtf/GATK-OVTF-Notebook.ipynb b/terra-jupyter-gatk-ovtf/GATK-OVTF-Notebook.ipynb deleted file mode 100644 index 1ccd9485..00000000 --- a/terra-jupyter-gatk-ovtf/GATK-OVTF-Notebook.ipynb +++ /dev/null @@ -1,104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# OpenVINO integration with TensorFlow on GATK CNNScoreVariants\n", - "\n", - "This notebook demonstrates using OpenVINO integration with TensorFlow on GATK CNNScoreVariants to achieve an improved performance.\n", - "\n", - "1. First, we execute CNNScoreVariants on a sample input data to execute and observe the improved throughput.\n", - "\n", - "2. Then, we execute CNNScoreVariants using the same input data and parameters again. However, this time we disable OpenVINO integration with TensorFlow to compare the throughput with stock TensorFlow. \n", - "\n", - "***Note:*** This notebook is intented to be run with \"terra-docker-gatk-ovtf\" image." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## GATK CNNScoreVariants on OV-TF" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "!gatk CNNScoreVariants \\\n", - " -I gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/bams/g94982_chr20_1m_10m_bamout.bam \\\n", - " -V gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/vcfs/g94982_b37_chr20_1m_15871.vcf.gz \\\n", - " -R gs://gcp-public-data--broad-references/hg19/v0/Homo_sapiens_assembly19.fasta \\\n", - " -O my_2d_cnn_scored.vcf \\\n", - " --tensor-type read_tensor \\\n", - " --transfer-batch-size 256 \\\n", - " --inference-batch-size 256" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Baseline GATK CNNScoreVariants" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "!OPENVINO_TF_DISABLE=\"1\" \\\n", - " gatk CNNScoreVariants \\\n", - " -I gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/bams/g94982_chr20_1m_10m_bamout.bam \\\n", - " -V gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/vcfs/g94982_b37_chr20_1m_15871.vcf.gz \\\n", - " -R gs://gcp-public-data--broad-references/hg19/v0/Homo_sapiens_assembly19.fasta \\\n", - " -O my_2d_cnn_scored_ovtf.vcf \\\n", - " --tensor-type read_tensor \\\n", - " --transfer-batch-size 256 \\\n", - " --inference-batch-size 256" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/terra-jupyter-gatk-ovtf/README.md b/terra-jupyter-gatk-ovtf/README.md deleted file mode 100644 index 90ba9397..00000000 --- a/terra-jupyter-gatk-ovtf/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# terra-jupyter-gatk-ovtf image - -This repo contains the `terra-jupyter-gatk-ovtf` image that is compatible with notebook service in [Terra](https://app.terra.bio/) called Leonardo. - -To use this in [Terra](https://app.terra.bio/), use `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-gatk-ovtf:` as image identifier. See [CHANGELOG.md](CHANGELOG.md) for the most recent tag. - -In Terra, this image is available in the drop-down menu under **COMMUNITY-MAINTAINED JUPYTER ENVIRONMENTS** - - - - -## Image contents - -The `terra-jupyter-gatk-ovtf` docker image extends the [terra-jupyter-python](../terra-jupyter-python/README.md) and [terra-jupyter-r](../terra-jupyter-r/README.md) by including the following: - -- Open JDK -- GATK -- Samtools -- [OpenVINO integration with TensorFlow](https://github.com/openvinotoolkit/openvino_tensorflow) (OVTF) - -To see the complete contents of this image please see the [Dockerfile](./Dockerfile). - -## Sample Notebook - -This repo provides a sample notebook [GATK-OVTF-Notebook.ipynb](./GATK-OVTF-Notebook.ipynb) which showcases the performance benefits obtained by using OpenVINOâ„¢ integration with TensorFlow. - -## Note on CUDA-enabled GPU devices Support - -Enabling OpenVINOâ„¢ integration with TensorFlow disables TensorFlow GPU support for CUDA-enabled devices in this docker image. -If you prefer to use CUDA-enabled GPU devices, DONOT import openvino_tensorflow - -#### Scenario #1: Use CUDA-enabled GPU devices with native Tensorflow. -After installing CUDA drivers in the host machine, you will need to start the docker with `--gpus all` to enable GPU devices inside docker container. - -Example: `docker run --gpus all --rm -it -p 8000:8000 terra-jupyter-gatk-ovtf`, Then navigate a browser to http://localhost:8000/notebooks to access the Jupyter UI. - -``` -import tensorflow as tf -# CPU and GPU support available. openvino_tensorflow is disabled -tf.config.list_physical_devices() -``` - -#### Scenario #2: Use OpenVINOâ„¢ integration with TensorFlow -``` -import tensorflow as tf -import openvino_tensorflow as ovtf -ovtf.set_backend("CPU") - -# By importing openvino_tensorflow, CUDA-enabled GPU devices support is disabled. -# To re-enable GPU support, restart the Jupyter kernel, and remove "import openvino_tensorflow as ovtf" -``` - -#### Scenario #3: Use GATK with CUDA-enabled GPU devices -In this Docker image, GATK is enabled with OpenVINOâ„¢ integration with TensorFlow by default. -If you prefer to use GATK with CUDA-enabled GPU devices, then OpenVINO integration with TensorFlow should be disabled before executing GATK CNNScoreVariants. To disable OpenVINOâ„¢ integration with TensorFlow, run the command below in the Jupyter notebook. -``` -! export OPENVINO_TF_DISABLE=1 -``` - -#### Scenario #4: Use OpenVINOâ„¢ integration with TensorFlow with CUDA-enabled GPU devices - -Not Supported. - ---------- - - -## Build and Run Instructions - **Locally** - -- ### Clone the repository - - ```bash - git clone https://github.com/DataBiosphere/terra-docker - - ``` - -- ### Build the docker image - - ```bash - cd terra-docker/terra-jupyter-gatk-ovtf - docker build . -t terra-jupyter-gatk-ovtf - ``` - -- ### Run the container, Then navigate a browser to http://localhost:8000/notebooks to access the Jupyter UI - - ```bash - docker run --rm -it -p 8000:8000 terra-jupyter-gatk-ovtf - ``` - -- ### You can gain root access and open a bash terminal as follows: - - ```bash - docker run --rm -it -u root -p 8000:8000 --entrypoint /bin/bash terra-jupyter-gatk-ovtf - ``` - -## Selecting prior versions of this image - -To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) for a specific package version you need. - -Once you find an image version that you want, simply copy and paste the image url from the changelog into the corresponding custom docker field in the Terra notebook runtime widget. diff --git a/terra-jupyter-gatk-ovtf/build.sh b/terra-jupyter-gatk-ovtf/build.sh deleted file mode 100755 index fd6bc89b..00000000 --- a/terra-jupyter-gatk-ovtf/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - - -docker build . -t iotgedge/terra-jupyter-gatk-ovtf diff --git a/terra-jupyter-gatk-ovtf/gatk-ovtf.patch b/terra-jupyter-gatk-ovtf/gatk-ovtf.patch deleted file mode 100644 index 2604c69f..00000000 --- a/terra-jupyter-gatk-ovtf/gatk-ovtf.patch +++ /dev/null @@ -1,104 +0,0 @@ -diff -ru vqsr_cnn/inference.py /opt/conda/lib/python3.7/site-packages/vqsr_cnn/vqsr_cnn/inference.py ---- vqsr_cnn/inference.py 2019-01-29 15:50:10.000000000 +0000 -+++ /opt/conda/lib/python3.7/site-packages/vqsr_cnn/vqsr_cnn/inference.py 2021-09-22 19:47:00.000000000 +0000 -@@ -11,6 +11,11 @@ - # Keras Imports - import keras - import keras.backend as K -+import tensorflow as tf -+from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 -+ -+import openvino_tensorflow -+openvino_tensorflow.set_backend('CPU') - - # Package Imports - from . import defines -@@ -38,7 +43,9 @@ - VARIANT_TYPE_FIFO_INDEX = 6 - VARIANT_FIFO_FIELDS = 7 - -- -+session = None -+prob_tensor = None -+full_model = None - - CIGAR_CODES_TO_COUNT = [ - defines.CIGAR_CODE['M'], defines.CIGAR_CODE['I'], defines.CIGAR_CODE['S'], defines.CIGAR_CODE['D'] -@@ -86,6 +93,11 @@ - variant_types = [] - variant_data = [] - read_batch = [] -+ -+ global session -+ global prob_tensor -+ global full_model -+ - for _ in range(batch_size): - fifo_line = tool.readDataFIFO() - fifo_data = fifo_line.split(defines.DATA_TYPE_SEPARATOR) -@@ -126,8 +138,24 @@ - predictions = model.predict([np.array(reference_batch), np.array(annotation_batch)], - batch_size=python_batch_size) - elif tensor_type in defines.TENSOR_MAPS_2D: -- predictions = model.predict( -- [np.array(read_batch), np.array(annotation_batch)], batch_size=python_batch_size) -+ if session is None: -+ full_model = tf.function(lambda x: model(x)) -+ full_model = full_model.get_concrete_function( -+ (tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype, name="read_tensor"), -+ tf.TensorSpec(model.inputs[1].shape, model.inputs[1].dtype, name="best_practices"))) -+ frozen_func = convert_variables_to_constants_v2(full_model) -+ frozen_func.graph.as_graph_def() -+ session = tf.compat.v1.Session(graph=frozen_func.graph) -+ prob_tensor = frozen_func.graph.get_tensor_by_name("model_1/softmax_predictions/Softmax:0") -+ for i in range(python_batch_size-batch_size): -+ tensor = np.empty(read_batch[0].shape) -+ read_batch.append(tensor) -+ tensor = np.empty(annotation_batch[0].shape) -+ annotation_batch.append(tensor) -+ batch = {} -+ batch["read_tensor:0"] = np.array(read_batch) -+ batch["best_practices:0"] = np.array(annotation_batch) -+ predictions = session.run(prob_tensor, feed_dict=batch) - else: - raise ValueError('Unknown tensor mapping. Check architecture file.', tensor_type) - -diff -ru vqsr_cnn/models.py /opt/conda/lib/python3.7/site-packages/vqsr_cnn/vqsr_cnn/models.py ---- vqsr_cnn/models.py 2019-01-29 15:50:10.000000000 +0000 -+++ /opt/conda/lib/python3.7/site-packages/vqsr_cnn/vqsr_cnn/models.py 2021-09-22 19:47:00.000000000 +0000 -@@ -4,12 +4,13 @@ - # Keras Imports - from keras import layers - from keras import metrics --import keras.backend as K --from keras.optimizers import Adam -+import tensorflow.compat.v1.keras.backend as K -+from keras.optimizers import adam_v2 as Adam - from keras.models import Model, load_model - from keras.layers.convolutional import Conv1D, Conv2D, MaxPooling1D, MaxPooling2D - from keras.callbacks import ModelCheckpoint, EarlyStopping, TensorBoard, ReduceLROnPlateau - from keras.layers import Input, Dense, Dropout, BatchNormalization, SpatialDropout1D, SpatialDropout2D, Activation, Flatten, AlphaDropout -+import tensorflow as tf - - from . import plots - from . import defines -@@ -20,9 +21,9 @@ - def start_session_get_args_and_model(intra_ops, inter_ops, semantics_json, weights_hd5=None, tensor_type=None): - K.clear_session() - K.get_session().close() -- cfg = K.tf.ConfigProto(intra_op_parallelism_threads=intra_ops, inter_op_parallelism_threads=inter_ops) -+ cfg = tf.compat.v1.ConfigProto(intra_op_parallelism_threads=intra_ops, inter_op_parallelism_threads=inter_ops) - cfg.gpu_options.allow_growth = True -- K.set_session(K.tf.Session(config=cfg)) -+ K.set_session(tf.compat.v1.Session(config=cfg)) - return args_and_model_from_semantics(semantics_json, weights_hd5, tensor_type) - - -@@ -609,4 +610,5 @@ - with open(json_file_name, 'w') as outfile: - json.dump(semantics, outfile) - -- print('Saved model semantics at:', json_file_name) -\ No newline at end of file -+ print('Saved model semantics at:', json_file_name) -+ diff --git a/terra-jupyter-gatk-ovtf/ovtf-cuda-disabling.patch b/terra-jupyter-gatk-ovtf/ovtf-cuda-disabling.patch deleted file mode 100644 index 169bbbe4..00000000 --- a/terra-jupyter-gatk-ovtf/ovtf-cuda-disabling.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- /opt/conda/lib/python3.7/site-packages/openvino_tensorflow/__init__.py 2021-12-09 22:39:36.000000000 +0000 -+++ __init__.py 2021-12-10 00:20:28.433852578 +0000 -@@ -30,6 +30,9 @@ - - import ctypes - -+if (os.environ.get("OPENVINO_TF_DISABLE") != "1"): -+ os.environ["CUDA_VISIBLE_DEVICES"] = "-1" -+ - __all__ = [ - 'enable', 'disable', 'is_enabled', 'list_backends', - 'set_backend', 'get_backend', diff --git a/terra-jupyter-gatk-ovtf/run_gatk.sh b/terra-jupyter-gatk-ovtf/run_gatk.sh deleted file mode 100644 index fa821efd..00000000 --- a/terra-jupyter-gatk-ovtf/run_gatk.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -gatk CNNScoreVariants \ - -I gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/bams/g94982_chr20_1m_10m_bamout.bam \ - -V gs://gatk-tutorials/workshop_2002/2-germline/CNNScoreVariants/vcfs/g94982_b37_chr20_1m_15871.vcf.gz \ - -R gs://gcp-public-data--broad-references/hg19/v0/Homo_sapiens_assembly19.fasta \ - -O my_2d_cnn_scored.vcf \ - --tensor-type read_tensor \ - --transfer-batch-size 256 \ - --inference-batch-size 256 diff --git a/terra-jupyter-gatk-ovtf/terra-ovtf-gatk.png b/terra-jupyter-gatk-ovtf/terra-ovtf-gatk.png deleted file mode 100644 index 332ffb549a0a0b56f16a7b9395b8fc5d750ff080..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257491 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⋘%6RT0GtKWG( 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

0Fy% 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'`. To work-around this uninstall the dependency so that flag `--use_rest_api` can be used - # with `%%bigquery` to use the older, slower mechanism for data transfer. - && pip3 uninstall -y google-cloud-bigquery-storage \ - && sed -i 's/pandas.lib/pandas/g' /opt/conda/lib/python3.7/site-packages/ggplot/stats/smoothers.py \ - # the next few `sed` lines are workaround for a ggplot bug. See https://github.com/yhat/ggpy/issues/662 - && sed -i 's/pandas.tslib.Timestamp/pandas.Timestamp/g' /opt/conda/lib/python3.7/site-packages/ggplot/stats/smoothers.py \ - && sed -i 's/pd.tslib.Timestamp/pd.Timestamp/g' /opt/conda/lib/python3.7/site-packages/ggplot/stats/smoothers.py \ - && sed -i 's/pd.tslib.Timestamp/pd.Timestamp/g' /opt/conda/lib/python3.7/site-packages/ggplot/utils.py + # Remove this after https://broadworkbench.atlassian.net/browse/CA-1179 + # As of release [google-cloud-bigquery 1.26.0 (2020-07-20)](https://github.com/googleapis/python-bigquery/blob/master/CHANGELOG.md#1260-2020-07-20) + # the BigQuery Python client uses the BigQuery Storage client by default. + # This currently causes an error on Terra Cloud Runtimes `the user does not have 'bigquery.readsessions.create' permission + # for ''`. To work-around this uninstall the dependency so that flag `--use_rest_api` can be used + # with `%%bigquery` to use the older, slower mechanism for data transfer. + && pip3 uninstall -y google-cloud-bigquery-storage \ + && sed -i 's/pandas.lib/pandas/g' /opt/conda/lib/python3.10/site-packages/ggplot/stats/smoothers.py \ + # the next few `sed` lines are workaround for a ggplot bug. See https://github.com/yhat/ggpy/issues/662 + && sed -i 's/pandas.tslib.Timestamp/pandas.Timestamp/g' /opt/conda/lib/python3.10/site-packages/ggplot/stats/smoothers.py \ + && sed -i 's/pd.tslib.Timestamp/pd.Timestamp/g' /opt/conda/lib/python3.10/site-packages/ggplot/stats/smoothers.py \ + && sed -i 's/pd.tslib.Timestamp/pd.Timestamp/g' /opt/conda/lib/python3.10/site-packages/ggplot/utils.py -RUN pip3 install --upgrade markupsafe==2.0.1 +RUN pip3 install --upgrade markupsafe==2.1.2 ENV USER jupyter USER $USER diff --git a/terra-jupyter-python/requirements.txt b/terra-jupyter-python/requirements.txt index d0b1cac2..6a0dcd29 100644 --- a/terra-jupyter-python/requirements.txt +++ b/terra-jupyter-python/requirements.txt @@ -21,12 +21,8 @@ cycler h5py html5lib joblib -# Remove the version pin for keras when tensorflow is updated to 2.7 or higher. -keras==2.7.0 patsy pymc3 -# fix to 2.4.7 so that tensorflow still works -pyparsing==2.4.7 Cython pysam --no-binary=pysam @@ -44,4 +40,4 @@ plotnine google-resumable-media #adding intel optimized xgboost and intel extension for scikit-learn scikit-learn-intelex -xgboost +xgboost \ No newline at end of file diff --git a/terra-jupyter-python/tests/smoke_test.ipynb b/terra-jupyter-python/tests/smoke_test.ipynb index 9902f1c3..7d2e9037 100644 --- a/terra-jupyter-python/tests/smoke_test.ipynb +++ b/terra-jupyter-python/tests/smoke_test.ipynb @@ -85,9 +85,9 @@ "metadata": {}, "outputs": [], "source": [ - "output = !pip3 show rich\n", + "output = !pip3 show pendulum\n", "print(output) # Should show not yet installed.\n", - "assert(0 == output.count('Name: rich'))" + "assert(0 == output.count('Name: pendulum'))" ] }, { @@ -96,7 +96,7 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install rich==10.16.1" + "%pip install pendulum==2.1.2" ] }, { @@ -105,9 +105,9 @@ "metadata": {}, "outputs": [], "source": [ - "output = !pip3 show rich\n", + "output = !pip3 show pendulum\n", "print(output) # Should show that it is now installed!\n", - "assert(1 == output.count('Name: rich'))" + "assert(1 == output.count('Name: pendulum'))" ] }, { @@ -123,9 +123,10 @@ "metadata": {}, "outputs": [], "source": [ - "output = !pip3 show docstring-parser\n", + "# python setup.py install\n", + "output = !pip3 show thefuzz\n", "print(output) # Should show not yet installed.\n", - "assert(0 == output.count('Name: docstring-parser'))" + "assert(0 == output.count('Name: thefuzz'))" ] }, { @@ -134,7 +135,7 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install docstring-parser==0.13" + "%pip install thefuzz" ] }, { @@ -143,9 +144,9 @@ "metadata": {}, "outputs": [], "source": [ - "output = !pip3 show docstring-parser\n", + "output = !pip3 show thefuzz\n", "print(output) # Should show that it is now installed!\n", - "assert(1 == output.count('Name: docstring-parser'))" + "assert(1 == output.count('Name: thefuzz'))" ] }, { @@ -401,6 +402,13 @@ "gsutil ls gs://gcp-public-data--gnomad" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -516,8 +524,15 @@ "outputs": [], "source": [ "import tensorflow as tf\n", - "import keras\n", - "\n", + "import keras" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "print(\"TensorFlow version:\", tf.__version__)\n", "print(\"Keras version:\", keras.__version__)\n", "print(\"TensorFlow executing_eagerly:\", tf.executing_eagerly())" @@ -867,7 +882,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.12" + "version": "3.10.9" }, "toc": { "base_numbering": 1, diff --git a/terra-jupyter-python/tests/smoke_test.py b/terra-jupyter-python/tests/smoke_test.py index 6f65ade1..55ded191 100644 --- a/terra-jupyter-python/tests/smoke_test.py +++ b/terra-jupyter-python/tests/smoke_test.py @@ -9,10 +9,14 @@ import os import pytest +import pandas as pd +import numpy as np +import pandas_gbq +import tensorflow as tf +import pysam +from google.oauth2 import service_account def test_pandas(): - import pandas as pd - import numpy as np pd.DataFrame( { @@ -25,31 +29,30 @@ def test_pandas(): } ) -def test_pandas_gbq(): - import pandas_gbq +# def test_pandas_gbq(): - sql = """ - SELECT country_name, alpha_2_code - FROM `bigquery-public-data.utility_us.country_code_iso` - WHERE alpha_2_code LIKE 'A%' - """ - pandas_gbq.read_gbq(sql, project_id=os.environ['GOOGLE_PROJECT']) +# sql = """ +# SELECT country_name, alpha_2_code +# FROM `bigquery-public-data.utility_us.country_code_iso` +# WHERE alpha_2_code LIKE 'A%' +# """ +# # Credentials +# credentials = service_account.Credentials.from_service_account_file('/tmp/credentials.json') +# print(os.environ['GOOGLE_PROJECT']) +# pandas_gbq.read_gbq(sql, project_id=os.environ['GOOGLE_PROJECT'], credentials=credentials) def test_tensorflow_gfile(): - import tensorflow as tf tf.io.gfile.copy(src='gs://genomics-public-data/1000-genomes/other/sample_info/sample_info.csv', dst='/tmp/genomics-public-data-1000-genomes-sample_info.csv', overwrite=True) def test_tensorflow_hello_world(): - import tensorflow as tf print("TensorFlow version : ", tf.version.GIT_VERSION, tf.version.VERSION) hello = tf.constant('Hello, TensorFlow!') tf.print(hello) def test_pysam(): - import pysam # Create BAM file from scratch # Code stolen from https://pysam.readthedocs.io/en/latest/usage.html#creating-bam-cram-sam-files-from-scratch diff --git a/terra-jupyter-r/CHANGELOG.md b/terra-jupyter-r/CHANGELOG.md index d47665ae..90623c24 100644 --- a/terra-jupyter-r/CHANGELOG.md +++ b/terra-jupyter-r/CHANGELOG.md @@ -1,3 +1,47 @@ +## 2.2.1 - 2023-07-06 + +- Fix bug introduced in https://github.com/DataBiosphere/terra-docker/commit/4a5b4c9212aedcafa2f41fbeb2b161089341c578 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.2.1` + +## 2.2.0 - 2023-06-23 + +- Python upgrade 3.7 to 3.10 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.2.0` + +## 2.1.10 - 2023-06-01T17:49:47.585402026Z + +- Bioconductor 3.17 release with R 4.3 + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.10` + +## 2.1.9 - 2023-03-13T17:26:34.207471Z + +- Update `terra-jupyter-base` to `1.0.14` + - Include `jupyter lab build` step + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.9` + +## 2.1.8 - 2023-02-09T14:18:49.113028Z + +- Update `terra-jupyter-base` to `1.0.12` + - Add cloud.google.com gpg key. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.8` + +## 2.1.7 - 2022-11-22T20:49:44.016171853Z + +- Update Bioconductor for 3.16 release + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.7` + +## 2.1.6 - 2022-07-27T13:32:19.838903Z + +- Installs AnVIL and supporting packages. + +Image URL: `us.gcr.io/broad-dsp-gcr-public/terra-jupyter-r:2.1.6` + ## 2.1.5 - 2022-06-23T10:58:12.961300Z - Update `terra-jupyter-base` to `1.0.10` diff --git a/terra-jupyter-r/Dockerfile b/terra-jupyter-r/Dockerfile index e595550a..f94bc990 100644 --- a/terra-jupyter-r/Dockerfile +++ b/terra-jupyter-r/Dockerfile @@ -1,17 +1,18 @@ -FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:1.0.11 +FROM us.gcr.io/broad-dsp-gcr-public/terra-jupyter-base:1.1.1 + USER root COPY scripts $JUPYTER_HOME/scripts # Add env vars to identify binary package installation -ENV TERRA_R_PLATFORM="terra-jupyter-r" -ENV TERRA_R_PLATFORM_BINARY_VERSION=3.15.0 +ENV TERRA_R_PLATFORM="terra-jupyter-r-1.1.1" +ENV TERRA_R_PLATFORM_BINARY_VERSION=3.17 -# Install protobuf 3.19.1. Note this version comes from base deep learning image. Use `conda list` to see what's installed +# Install protobuf 3.20.3. Note this version comes from base deep learning image. Use `conda list` to see what's installed RUN cd /tmp \ - && wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.1/protobuf-all-3.19.1.tar.gz \ - && tar -xvzf protobuf-all-3.19.1.tar.gz \ - && cd protobuf-3.19.1/ \ + && wget https://github.com/protocolbuffers/protobuf/releases/download/v3.20.3/protobuf-all-3.20.3.tar.gz \ + && tar -xvzf protobuf-all-3.20.3.tar.gz \ + && cd protobuf-3.20.3/ \ && ./configure \ && make \ && make check \ @@ -96,6 +97,12 @@ RUN apt-get update \ libzmq3-dev \ libsbml5-dev \ biber \ + ## for gpuMagic package + ocl-icd-opencl-dev \ + ## for ChemmineOB package + libeigen3-dev \ + ## for rawrr package + mono-runtime \ && ln -s /usr/lib/gcc/x86_64-linux-gnu/7/libgfortran.so /usr/lib/x86_64-linux-gnu/libgfortran.so \ && ln -s /usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.so /usr/lib/x86_64-linux-gnu/libstdc++.so \ && apt-get clean \ @@ -121,7 +128,7 @@ RUN pip3 -V \ RUN R -e 'install.packages("BiocManager")' \ ## check version - && R -e 'BiocManager::install(version="3.15", ask=FALSE)' \ + && R -e 'BiocManager::install(version="3.17", ask=FALSE)' \ && R -e 'BiocManager::install(c( \ "boot", \ "class", \ @@ -153,11 +160,27 @@ RUN R -e 'install.packages("BiocManager")' \ "pbdZMQ", \ "uuid"))' \ && R -e 'BiocManager::install("DataBiosphere/Ronaldo")' + +# Install Bioconductor packages found at: +# https://raw.githubusercontent.com/anvilproject/anvil-docker/master/anvil-rstudio-bioconductor/install.R +RUN R -e 'BiocManager::install(c( \ + "AnVIL", \ + "SingleCellExperiment", \ + "GenomicFeatures", \ + "GenomicAlignments", \ + "ShortRead", \ + "DESeq2", \ + "AnnotationHub", \ + "ExperimentHub", \ + "ensembldb", \ + "scRNAseq", \ + "scran", \ + "Rtsne"))' ## pip runs as jupyter user ENV PIP_USER=true RUN R -e 'IRkernel::installspec(user=FALSE)' \ - && chown -R $USER:users /usr/local/lib/R/site-library + && chown -R $USER:users /usr/local/lib/R/site-library /home/jupyter USER $USER diff --git a/terra-jupyter-r/tests/smoke_test.ipynb b/terra-jupyter-r/tests/smoke_test.ipynb new file mode 100644 index 00000000..f346c97c --- /dev/null +++ b/terra-jupyter-r/tests/smoke_test.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3d33c4ec", + "metadata": {}, + "source": [ + "Test Bioconductor image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52ca0aa1", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('assertthat')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "martial-fitness", + "metadata": {}, + "outputs": [], + "source": [ + "require(assertthat)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "blessed-burden", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::version()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cde5e74", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install(\"rsbml\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc54067c", + "metadata": {}, + "outputs": [], + "source": [ + "library(rsbml)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "699ca08a", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install(\"RCurl\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91f1d7ad", + "metadata": {}, + "outputs": [], + "source": [ + "library(RCurl)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b941edc1", + "metadata": {}, + "outputs": [], + "source": [ + "library(\"SingleCellExperiment\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3adf1f30", + "metadata": {}, + "outputs": [], + "source": [ + "# This will be fixed in bioc (R 4.0)\n", + "# library(\"DESeq2\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bibliographic-intent", + "metadata": {}, + "outputs": [], + "source": [ + "# assert_that('DESeq2' %in% rownames(installed.packages()) == 'TRUE')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3663e1de", + "metadata": {}, + "outputs": [], + "source": [ + "library(\"ShortRead\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c2881aa", + "metadata": {}, + "outputs": [], + "source": [ + "library(\"GenomicAlignments\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15a5ba29", + "metadata": {}, + "outputs": [], + "source": [ + "library(\"GenomicFeatures\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45051b29", + "metadata": {}, + "outputs": [], + "source": [ + "find.package('ShortRead')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09a7477d", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('XML')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c33a766b", + "metadata": {}, + "outputs": [], + "source": [ + "library(XML)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c90bd714", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('Rgraphviz')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ba20610", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('BiocSklearn')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cd7e307", + "metadata": {}, + "outputs": [], + "source": [ + "library('BiocSklearn')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbb3f21b", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('rhdf5')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "919ab35c", + "metadata": {}, + "outputs": [], + "source": [ + "library(rhdf5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7e397b2", + "metadata": {}, + "outputs": [], + "source": [ + "# Currently broken\n", + "# BiocManager::install('ChemmineOB')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3d238c6", + "metadata": {}, + "outputs": [], + "source": [ + "# library(ChemmineOB)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dad1ef3", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('EBImage')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4749b448", + "metadata": {}, + "outputs": [], + "source": [ + "library(EBImage)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdb05268", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('RMySQL')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa8c5778", + "metadata": {}, + "outputs": [], + "source": [ + "library(RMySQL)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "592faa65", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('DirichletMultinomial')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e87ffece", + "metadata": {}, + "outputs": [], + "source": [ + "library(DirichletMultinomial)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3e13a29", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('rjags')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72a7c4d4", + "metadata": {}, + "outputs": [], + "source": [ + "library('rjags')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "invisible-romantic", + "metadata": {}, + "outputs": [], + "source": [ + "BiocManager::install('testit')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3c864c5", + "metadata": {}, + "outputs": [], + "source": [ + "# Comment out for now\n", + "BiocManager::install('protolite')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a4b7b40", + "metadata": {}, + "outputs": [], + "source": [ + "library(protolite)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "younger-greenhouse", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert_that(\"GenomicFeatures\" %in% installed.packages() == 'TRUE')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "approximate-cutting", + "metadata": {}, + "outputs": [], + "source": [ + "assert_that(\"SingleCellExperiment\" %in% installed.packages() == 'TRUE')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "special-fishing", + "metadata": {}, + "outputs": [], + "source": [ + "assert_that(\"GenomicAlignments\" %in% installed.packages() == 'TRUE')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "conscious-hacker", + "metadata": {}, + "outputs": [], + "source": [ + "assert_that(\"ShortRead\" %in% installed.packages() == 'TRUE')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f3c92ba", + "metadata": {}, + "outputs": [], + "source": [ + "assert_that(\"Seurat\" %in% installed.packages() == 'TRUE')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "R", + "language": "R", + "name": "ir" + }, + "language_info": { + "codemirror_mode": "r", + "file_extension": ".r", + "mimetype": "text/x-r-source", + "name": "R", + "pygments_lexer": "r", + "version": "4.1.0" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/terra-rstudio-aou/CHANGELOG.md b/terra-rstudio-aou/CHANGELOG.md new file mode 100644 index 00000000..3a3b4bf3 --- /dev/null +++ b/terra-rstudio-aou/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## 0.1.1 - 2023-03-16 + +* Install Wondershaper + +## 3.16.0 - 2023-01-29 + +* First release diff --git a/terra-rstudio-aou/Dockerfile b/terra-rstudio-aou/Dockerfile new file mode 100644 index 00000000..925fadeb --- /dev/null +++ b/terra-rstudio-aou/Dockerfile @@ -0,0 +1,65 @@ +# Base this image on the Terra AnVIL RStudio-Bioconductor image. +FROM us.gcr.io/broad-dsp-gcr-public/anvil-rstudio-bioconductor:3.16.0 + +ENV AOU_DOCKER_VERSION=0.1.1 +LABEL vendor="The All Of Us Research Program" \ + description="RStudio + Bioconductor docker image for use in the All of Us Researcher Workbench." \ + license="GPL-2.0-or-later" \ + version=$AOU_DOCKER_VERSION \ + url="https://github.com/DataBiosphere/terra-docker" + +# BUG: Update broken google-cloud GPG key preventing +# apt-get update to complete +RUN curl -O https://packages.cloud.google.com/apt/doc/apt-key.gpg \ + && apt-key add apt-key.gpg + +# DEBUG: install speedtest-cli to ascertain tc bandwidth throttling is working +RUN apt-get update \ + && apt-get install -yq --no-install-recommends \ + iproute2 \ + speedtest-cli \ + && echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections \ + && echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections \ + && apt-get -y install iptables-persistent \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Compile from source during image construction to prevent loading +# packages that have not been updated recently. +ENV BIOCONDUCTOR_USE_CONTAINER_REPOSITORY=FALSE + +# Install tidyverse +COPY rstudio/scripts/install_tidyverse.sh /rocker_scripts/install_tidyverse.sh +RUN chmod +x /rocker_scripts/install_tidyverse.sh && /rocker_scripts/install_tidyverse.sh + +# Install top-25 most common packages +# This step is very slow and we want to cache it as much as possible to prevent +# users from having a bad experience. +COPY rstudio/scripts/install_bioconductor.R /rocker_scripts/install_bioconductor.R +RUN R -f /rocker_scripts/install_bioconductor.R + +# Use binary repos to speed up package installation. +ENV BIOCONDUCTOR_USE_CONTAINER_REPOSITORY=TRUE + +# Add our rserver configuration +ADD rstudio/rserver.conf $RSTUDIO_HOME/rserver.conf + +# Install Wondershaper from source, for client-side egress limiting. +RUN cd /usr/local/share && \ + git clone https://github.com/magnific0/wondershaper.git --depth 1 && \ + cd wondershaper && \ + make install && \ + cd $HOME + +# Expose ports 8787 and disable authentication as this is handled +# internally. Root access/super powers are disabled. +ENV RSTUDIO_PORT 8787 +ENV DISABLE_AUTH=true +EXPOSE $RSTUDIO_PORT + +# Hide tc etc from users +RUN chmod 700 -R /usr/sbin/ + +COPY rstudio/entrypoint.sh /init_aou +RUN chmod +x /init_aou +ENTRYPOINT [ "/init_aou" ] diff --git a/terra-rstudio-aou/README.md b/terra-rstudio-aou/README.md new file mode 100644 index 00000000..cc95ac25 --- /dev/null +++ b/terra-rstudio-aou/README.md @@ -0,0 +1,19 @@ +# terra-rstudio-aou image + +This repo contains the terra-rstudio-aou image that is compatible with the notebook service in [Terra]("https://app.terra.bio/") called Leonardo. For example, use `us.gcr.io/broad-dsp-gcr-public/terra-rstudio-aou:{version}` in Terra. + +This image is intended to support usage in the [All of Us Workbench](https://github.com/all-of-us/workbench). + +## Image contents + +The terra-rstudio-aou extends the [AnVIL Bioconductor image](https://github.com/anvilproject/anvil-docker) by including the following: + +- Configuring `wondershaper` to limit network bandwidth. + +To see the complete contents of this image please see the [Dockerfile](./Dockerfile). + +## Selecting prior versions of this image + +To select an older version this image, you can search the [CHANGELOG.md](./CHANGELOG.md) for a specific package version you need. + +Once you find an image version that you want, copy and paste the image url from the changelog into the corresponding custom Docker field in the Terra notebook runtime widget. diff --git a/terra-rstudio-aou/VERSION b/terra-rstudio-aou/VERSION new file mode 100644 index 00000000..6da28dde --- /dev/null +++ b/terra-rstudio-aou/VERSION @@ -0,0 +1 @@ +0.1.1 \ No newline at end of file diff --git a/terra-rstudio-aou/rstudio/entrypoint.sh b/terra-rstudio-aou/rstudio/entrypoint.sh new file mode 100644 index 00000000..499829d7 --- /dev/null +++ b/terra-rstudio-aou/rstudio/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e # catches errors + +pushd /usr/local/share/wondershaper +wondershaper -a "eth0" -u 16384 # kilobits; 16Mib/s (2MiB/s) + +popd + +# Init must be run as root and will internally handle mapping the end-user +# to UID and GID mapping using S6-overlay +# https://github.com/just-containers/s6-overlay +# using S6 +# https://skarnet.org/software/s6/overview.html +exec /init diff --git a/terra-rstudio-aou/rstudio/rserver.conf b/terra-rstudio-aou/rstudio/rserver.conf new file mode 100644 index 00000000..c7d9860e --- /dev/null +++ b/terra-rstudio-aou/rstudio/rserver.conf @@ -0,0 +1,7 @@ +# Server Configuration File +# See https://support.rstudio.com/hc/en-us/articles/200552316-Configuring-the-Server + +rsession-which-r=/usr/local/bin/R +auth-none=1 +www-port=8787 +www-address=0.0.0.0 diff --git a/terra-rstudio-aou/rstudio/scripts/install_bioconductor.R b/terra-rstudio-aou/rstudio/scripts/install_bioconductor.R new file mode 100644 index 00000000..e850a2e0 --- /dev/null +++ b/terra-rstudio-aou/rstudio/scripts/install_bioconductor.R @@ -0,0 +1,50 @@ +options(Ncpus = parallel::detectCores()) + +BiocManager::install(c( + 'AnVIL', + 'DataBiosphere/Ronaldo', + 'shiny', + 'bigrquery', + 'googleCloudStorageR', + 'tidyverse', + 'Seurat', + "SingleCellExperiment", + "GenomicFeatures", + "GenomicAlignments", + "ShortRead", + "DESeq2", + "AnnotationHub", + "ExperimentHub", + "ensembldb", + "scRNAseq", + "scran", + "Rtsne", + 'BiocGenerics', + 'S4Vectors', + 'BiocVersion', + 'GenomeInfoDb', + 'IRanges', + 'Biobase', + 'zlibbioc', + 'Biostrings', + 'BiocParallel', + 'GenomicRanges', + 'DelayedArray', + 'SummarizedExperiment', + 'MatrixGenerics', + 'limma', + 'AnnotationDbi', + 'KEGGREST', + 'annotate', + 'BiocFileCache', + 'genefilter', + 'Rhtslib', + 'Rsamtools', + 'edgeR', + 'biomaRt', + 'geneplotter', + 'ggtree', + 'rhdf5filters', + 'fgsea', + 'Rgraphviz' + )) diff --git a/terra-rstudio-aou/rstudio/scripts/install_tidyverse.sh b/terra-rstudio-aou/rstudio/scripts/install_tidyverse.sh new file mode 100644 index 00000000..4281290f --- /dev/null +++ b/terra-rstudio-aou/rstudio/scripts/install_tidyverse.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +set -e + +## build ARGs +NCPUS=${NCPUS:--1} + +# a function to install apt packages only if they are not installed +function apt_install() { + if ! dpkg -s "$@" >/dev/null 2>&1; then + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + apt-get update + fi + apt-get install -y --no-install-recommends "$@" + fi +} + +apt_install \ + libxml2-dev \ + libcairo2-dev \ + libgit2-dev \ + default-libmysqlclient-dev \ + libpq-dev \ + libsasl2-dev \ + libsqlite3-dev \ + libssh2-1-dev \ + libxtst6 \ + libcurl4-openssl-dev \ + libharfbuzz-dev \ + libfribidi-dev \ + libfreetype6-dev \ + libpng-dev \ + libtiff5-dev \ + libjpeg-dev \ + unixodbc-dev + +install2.r --error --skipinstalled -n "$NCPUS" \ + tidyverse \ + devtools \ + rmarkdown \ + BiocManager \ + vroom \ + gert + +## dplyr database backends +install2.r --error --skipmissing --skipinstalled -n "$NCPUS" \ + arrow \ + dbplyr \ + DBI \ + dtplyr \ + duckdb \ + nycflights13 \ + Lahman \ + RMariaDB \ + RPostgres \ + RSQLite \ + fst + +## a bridge to far? -- brings in another 60 packages +# install2.r --error --skipinstalled -n "$NCPUS" tidymodels + +# Clean up +rm -rf /var/lib/apt/lists/* +rm -rf /tmp/downloaded_packages + +## Strip binary installed lybraries from RSPM +## https://github.com/rocker-org/rocker-versioned2/issues/340 +strip /usr/local/lib/R/site-library/*/libs/*.so + +# Check the tidyverse core packages' version +echo -e "Check the tidyverse package...\n" + +R -q -e "library(tidyverse)" + +echo -e "\nInstall tidyverse package, done!" diff --git a/terra-rstudio-aou/rstudio/tests/smoke_test.R b/terra-rstudio-aou/rstudio/tests/smoke_test.R new file mode 100755 index 00000000..8790327e --- /dev/null +++ b/terra-rstudio-aou/rstudio/tests/smoke_test.R @@ -0,0 +1,73 @@ +#! /usr/bin/env Rscript + +BiocManager::install("assertthat") +require(assertthat) +BiocManager::version() +BiocManager::install("rsbml") +library(rsbml) +BiocManager::install("RCurl") +library(RCurl) +library("ShortRead") +library("GenomicAlignments") +library("GenomicFeatures") +find.package("ShortRead") +BiocManager::install("XML") +library(XML) +BiocManager::install("EBImage") +library(EBImage) +BiocManager::install("RMySQL") +library(RMySQL) +BiocManager::install("DirichletMultinomial") +library(DirichletMultinomial) +BiocManager::install("rjags") +library("rjags") +BiocManager::install("testit") +BiocManager::install("protolite") +library(protolite) + +assert_that("AnVIL" %in% installed.packages() == "TRUE") +assert_that("Ronaldo" %in% installed.packages() == "TRUE") +assert_that("shiny" %in% installed.packages() == "TRUE") +assert_that("bigrquery" %in% installed.packages() == "TRUE") +assert_that("googleCloudStorageR" %in% installed.packages() == "TRUE") +assert_that("tidyverse" %in% installed.packages() == "TRUE") +assert_that("Seurat" %in% installed.packages() == "TRUE") +assert_that("SingleCellExperiment" %in% installed.packages() == "TRUE") +assert_that("GenomicFeatures" %in% installed.packages() == "TRUE") +assert_that("GenomicAlignments" %in% installed.packages() == "TRUE") +assert_that("ShortRead" %in% installed.packages() == "TRUE") +assert_that("DESeq2" %in% installed.packages() == "TRUE") +assert_that("AnnotationHub" %in% installed.packages() == "TRUE") +assert_that("ExperimentHub" %in% installed.packages() == "TRUE") +assert_that("ensembldb" %in% installed.packages() == "TRUE") +assert_that("scRNAseq" %in% installed.packages() == "TRUE") +assert_that("scran" %in% installed.packages() == "TRUE") +assert_that("Rtsne" %in% installed.packages() == "TRUE") +assert_that("BiocGenerics" %in% installed.packages() == "TRUE") +assert_that("S4Vectors" %in% installed.packages() == "TRUE") +assert_that("BiocVersion" %in% installed.packages() == "TRUE") +assert_that("GenomeInfoDb" %in% installed.packages() == "TRUE") +assert_that("IRanges" %in% installed.packages() == "TRUE") +assert_that("Biobase" %in% installed.packages() == "TRUE") +assert_that("zlibbioc" %in% installed.packages() == "TRUE") +assert_that("Biostrings" %in% installed.packages() == "TRUE") +assert_that("BiocParallel" %in% installed.packages() == "TRUE") +assert_that("GenomicRanges" %in% installed.packages() == "TRUE") +assert_that("DelayedArray" %in% installed.packages() == "TRUE") +assert_that("SummarizedExperiment" %in% installed.packages() == "TRUE") +assert_that("MatrixGenerics" %in% installed.packages() == "TRUE") +assert_that("limma" %in% installed.packages() == "TRUE") +assert_that("AnnotationDbi" %in% installed.packages() == "TRUE") +assert_that("KEGGREST" %in% installed.packages() == "TRUE") +assert_that("annotate" %in% installed.packages() == "TRUE") +assert_that("BiocFileCache" %in% installed.packages() == "TRUE") +assert_that("genefilter" %in% installed.packages() == "TRUE") +assert_that("Rhtslib" %in% installed.packages() == "TRUE") +assert_that("Rsamtools" %in% installed.packages() == "TRUE") +assert_that("edgeR" %in% installed.packages() == "TRUE") +assert_that("biomaRt" %in% installed.packages() == "TRUE") +assert_that("geneplotter" %in% installed.packages() == "TRUE") +assert_that("ggtree" %in% installed.packages() == "TRUE") +assert_that("rhdf5filters" %in% installed.packages() == "TRUE") +assert_that("fgsea" %in% installed.packages() == "TRUE") +assert_that("Rgraphviz" %in% installed.packages() == "TRUE") diff --git a/updateVersions.sc b/updateVersions.sc index a73a4408..ccc9003f 100755 --- a/updateVersions.sc +++ b/updateVersions.sc @@ -3,11 +3,10 @@ * Run it with `ammonite for Scala 2.13+` */ -val workbenchUtil2 = "0.10-c3ef6b80-SNAP" -val circeVersion = "0.13.0" +val circeVersion = "0.14.1" interp.load.ivy( - "co.fs2" %% "fs2-io" % "2.4.2", + "co.fs2" %% "fs2-io" % "2.5.10", "io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-parser" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, @@ -51,7 +50,7 @@ def modifyVersion(bumpMajorVersion: Boolean): Json => Json = newVersion(s, bumpMajorVersion) } -def modifyImageData(imagesToUpdate: List[String], bumpMajorVersion: Boolean): Json => Json = +def modifyImageData(imagesToUpdate: List[String], bumpMajorVersion: Boolean): Json => Json = root.image_data.arr.modify { listOfImageData => listOfImageData.map { @@ -78,21 +77,18 @@ def main(updatedImage: String, updatedImageReleaseNote: String, bumpMajorVersion "terra-jupyter-r", "terra-jupyter-gatk", "terra-jupyter-aou", - "terra-jupyter-gatk-ovtf" ) case "terra-jupyter-r" => List( "terra-jupyter-r", "terra-jupyter-bioconductor", "terra-jupyter-gatk", "terra-jupyter-aou", - "terra-jupyter-gatk-ovtf" ) case "terra-jupyter-python" => List( "terra-jupyter-python", "terra-jupyter-hail", "terra-jupyter-gatk", "terra-jupyter-aou", - "terra-jupyter-gatk-ovtf" ) case "terra-jupyter-hail" => List( "terra-jupyter-hail" @@ -100,8 +96,8 @@ def main(updatedImage: String, updatedImageReleaseNote: String, bumpMajorVersion case "terra-jupyter-gatk" => List( "terra-jupyter-gatk" ) - case "terra-jupyter-gatk-ovtf" => List( - "terra-jupyter-gatk-ovtf" + case "terra-jupyter-aou" => List( + "terra-jupyter-aou" ) case updatedImage => throw new Exception(s"${updatedImage} is not supported yet. Please update the script to support the image") diff --git a/wondershaper/Dockerfile b/wondershaper/Dockerfile new file mode 100644 index 00000000..30b01c33 --- /dev/null +++ b/wondershaper/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:latest + +# Install dependencies +RUN apt-get update && apt-get install -y \ + iproute2 \ + ethtool \ + wget \ + unzip \ + build-essential + +# Install Wondershaper +RUN apt-get install -y wget \ + && wget https://github.com/magnific0/wondershaper/archive/master.zip \ + && unzip master.zip \ + && rm master.zip \ + && cd wondershaper-master \ + && make install + +# Set up entrypoint +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] diff --git a/wondershaper/README.md b/wondershaper/README.md new file mode 100644 index 00000000..f3ab6e5f --- /dev/null +++ b/wondershaper/README.md @@ -0,0 +1,38 @@ +# Dockerfile and Entrypoint for Wondershaper +This Dockerfile and associated entrypoint script provide a way to set up and run Wondershaper, a network traffic shaping tool, within a Docker container. The Docker image is already published and available at us.gcr.io/wondershaper/wondershaper:latest. It is not necessary to republish the image unless modifications are required. + +## Dockerfile +The Dockerfile sets up an Ubuntu-based environment, installs the required dependencies, and installs Wondershaper from the official GitHub repository. It also includes an entrypoint script that configures the network interface and applies bandwidth limits using Wondershaper. + +To build the Docker image locally, use the following command: + +``` +docker build -t wondershaper-local-image . +``` + +To run the local image, use: +``` +docker run --privileged --network=host wondershaper-local-image +``` + +## Entrypoint Script (entrypoint.sh) +The entrypoint script (entrypoint.sh) is copied into the Docker image. It performs the following tasks: + +Sets the network interface (eth0) and the desired upload bandwidth limit (in kilobits per second). +Applies the bandwidth limits to the network interface using Wondershaper. +Keeps the container running by executing the tail -f /dev/null command. + +## Running the Docker Image +To run the Wondershaper container using the published image (us.gcr.io/wondershaper/wondershaper:latest), execute the following command: + +``` +docker run --name wondershaper-container us.gcr.io/broad-dsp-gcr-public/wondershaper:latest +``` + +Please note that the image is already published, so you don't need to perform any additional steps unless you want to modify the Dockerfile or the entrypoint script. + + +Remember to replace wondershaper-container with your preferred container name. + + + diff --git a/wondershaper/entrypoint.sh b/wondershaper/entrypoint.sh new file mode 100644 index 00000000..8c035aa4 --- /dev/null +++ b/wondershaper/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Network interface and bandwidth values +INTERFACE="eth0" +UPLOAD_BANDWIDTH="16384" # kilobits; 16Mib/s (2MiB/s) + +# Apply bandwidth limits using Wondershaper +wondershaper -a $INTERFACE -u $UPLOAD_BANDWIDTH + +# Keep the container running +tail -f /dev/null + +#!/bin/bash From c318bcaeabc395349f8fc9cc0cbeb4c716b8fcdd Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Thu, 31 Aug 2023 01:19:13 -0400 Subject: [PATCH 16/26] Update readmes and changelogs --- terra-jupyter-minimal-base/CHANGELOG.md | 12 ++++++++++++ terra-jupyter-minimal-base/README.md | 3 ++- terra-jupyter-minimal-gpu-base/CHANGELOG.md | 13 +++++++++++++ terra-jupyter-minimal-gpu-base/README.md | 7 ++++--- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/terra-jupyter-minimal-base/CHANGELOG.md b/terra-jupyter-minimal-base/CHANGELOG.md index 7b3cf848..c1b39737 100644 --- a/terra-jupyter-minimal-base/CHANGELOG.md +++ b/terra-jupyter-minimal-base/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.0.2 - 08/30/2023 + +- Extends Ubuntu 20.04 base image +- OS prerequisites +- google-cloud-cli +- Python 3.10 +- Miniconda +- Mamba +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + ## 0.0.1 - 08/30/2022 - Extends Ubuntu 20.04 base image diff --git a/terra-jupyter-minimal-base/README.md b/terra-jupyter-minimal-base/README.md index 6f904f28..76ea64a9 100644 --- a/terra-jupyter-minimal-base/README.md +++ b/terra-jupyter-minimal-base/README.md @@ -10,8 +10,9 @@ requirements necessary to set up Jupyter and provide compatibility with Leonardo - OS prerequisites - google-cloud-cli -- Python 3.7 +- Python 3.10 - Miniconda +- Mamba - Jupyter - Leonardo customizations/extensions - Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) diff --git a/terra-jupyter-minimal-gpu-base/CHANGELOG.md b/terra-jupyter-minimal-gpu-base/CHANGELOG.md index de618108..57d4d93c 100644 --- a/terra-jupyter-minimal-gpu-base/CHANGELOG.md +++ b/terra-jupyter-minimal-gpu-base/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.0.2 - 08/30/2023 + +- Extends `nvcr.io/nvidia/cuda:11.8.0-base-ubuntu20.04` base image + - CUDA 11.4 +- OS prerequisites +- google-cloud-cli +- Python 3.10 +- Miniconda +- Mamba +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + ## 0.0.1 - 08/30/2022 - Extends `nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04` base image diff --git a/terra-jupyter-minimal-gpu-base/README.md b/terra-jupyter-minimal-gpu-base/README.md index 0861e683..6c6233fb 100644 --- a/terra-jupyter-minimal-gpu-base/README.md +++ b/terra-jupyter-minimal-gpu-base/README.md @@ -6,15 +6,16 @@ notebook service in [Terra]("https://app.terra.bio/") called Leonardo. ## Image contents `terra-jupyter-minimal-gpu-base` extends an image from Nvidia built on top of -Ubuntu 20.04 called `nvcr.io/nvidia/cuda:11.3.1-base-ubuntu20.04`, which has CUDA and +Ubuntu 20.04 called `nvcr.io/nvidia/cuda:11.8.0-base-ubuntu20.04`, which has CUDA and Nvidia drivers installed. This image adds the minimum requirements necessary to set up Jupyter and provide compatibility with Leonardo. -- CUDA 11.3 +- CUDA 11.4 - OS prerequisites - google-cloud-cli -- Python 3.7 +- Python 3.10 - Miniconda +- Mamba - Jupyter - Leonardo customizations/extensions - Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) From f652d8461aea1b06de7d7ce3135ddfe0a34b1524 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 11:20:15 -0500 Subject: [PATCH 17/26] Updates to 0.0.3 Make libmamba solver default for conda Update google-cloud-cli version Update miniconda version Install ipykernel in conda to enable multiple jupyter kernels Update CUDA to 12.2.2 --- terra-jupyter-minimal-base/CHANGELOG.md | 14 ++++++++++++++ terra-jupyter-minimal-base/Dockerfile | 9 +++++++-- terra-jupyter-minimal-base/build_docker.sh | 2 +- terra-jupyter-minimal-gpu-base/CHANGELOG.md | 18 +++++++++++++++++- terra-jupyter-minimal-gpu-base/build_docker.sh | 4 ++-- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/terra-jupyter-minimal-base/CHANGELOG.md b/terra-jupyter-minimal-base/CHANGELOG.md index c1b39737..cf3da776 100644 --- a/terra-jupyter-minimal-base/CHANGELOG.md +++ b/terra-jupyter-minimal-base/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.0.3 - 11/08/2023 + +- Extends Ubuntu 20.04 base image +- OS prerequisites +- google-cloud-cli +- Python 3.10 +- Miniconda + - installs libmamba solver and makes it default ( + [see here](https://www.anaconda.com/blog/a-faster-conda-for-a-growing-community/)) +- Mamba +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + ## 0.0.2 - 08/30/2023 - Extends Ubuntu 20.04 base image diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index e289d7d0..401cdd82 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -10,13 +10,14 @@ USER root # files display nicer when viewed in a terminal. ENV DEBIAN_FRONTEND=noninteractive \ LC_ALL=en_US.UTF-8 \ - GOOGLE_CLOUD_CLI_VERSION="444.0.0" \ + GOOGLE_CLOUD_CLI_VERSION="454.0.0" \ USER=jupyter \ WELDER_USER=welder-user \ WELDER_UID=1001 \ # ensure this matches c.NotebookApp.port in jupyter_notebook_config.py JUPYTER_PORT=8000 \ JUPYTER_HOME=/etc/jupyter \ + MINICONDA_VERSION="py310_23.9.0-0" \ CONDA_AUTO_UPDATE_CONDA=false \ CONDA_DIR=/opt/conda \ GCLOUD_DIR=/opt/gcloud @@ -69,7 +70,7 @@ RUN useradd -m -s /bin/bash $USER \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ # Install miniconda to $CONDA_DIR - && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py310_23.5.1-0-Linux-x86_64.sh \ + && curl -so $HOME/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-${MINICONDA_VERSION}-Linux-x86_64.sh \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ @@ -111,8 +112,12 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba + && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ + && conda config --set solver libmamba \ && conda install -y -c conda-forge mamba==1.5.0 \ && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ +# Enable dropdown menu showing all conda envs as jupyter kernels + && conda install -c anaconda ipykernel \ # Cleanup && rm -rf ~/.cache/pip \ && conda clean -yaf \ diff --git a/terra-jupyter-minimal-base/build_docker.sh b/terra-jupyter-minimal-base/build_docker.sh index ea8c6ffb..f4605ee7 100755 --- a/terra-jupyter-minimal-base/build_docker.sh +++ b/terra-jupyter-minimal-base/build_docker.sh @@ -6,7 +6,7 @@ cp -r ../terra-jupyter-base/custom custom cp -r ../terra-jupyter-base/scripts scripts cp -r ../terra-jupyter-base/jupyter_notebook_config.py jupyter_notebook_config.py -docker build -t terra-jupyter-minimal-base:0.0.2 . +docker build -t terra-jupyter-minimal-base:0.0.3 . rm -r custom rm -r scripts diff --git a/terra-jupyter-minimal-gpu-base/CHANGELOG.md b/terra-jupyter-minimal-gpu-base/CHANGELOG.md index 57d4d93c..83d55274 100644 --- a/terra-jupyter-minimal-gpu-base/CHANGELOG.md +++ b/terra-jupyter-minimal-gpu-base/CHANGELOG.md @@ -1,7 +1,23 @@ +## 0.0.3 - 11/08/2023 + +- Extends `nvcr.io/nvidia/cuda:12.2.2-base-ubuntu20.04` base image + - CUDA 12.2 +- OS prerequisites +- google-cloud-cli +- Python 3.10 +- Miniconda + - installs libmamba solver and makes it default ( + [see here](https://www.anaconda.com/blog/a-faster-conda-for-a-growing-community/)) +- Mamba +- Jupyter +- Leonardo customizations/extensions +- Terra python client library ([FISS](https://github.com/broadinstitute/fiss)) + + ## 0.0.2 - 08/30/2023 - Extends `nvcr.io/nvidia/cuda:11.8.0-base-ubuntu20.04` base image - - CUDA 11.4 + - CUDA 11.8 - OS prerequisites - google-cloud-cli - Python 3.10 diff --git a/terra-jupyter-minimal-gpu-base/build_docker.sh b/terra-jupyter-minimal-gpu-base/build_docker.sh index 61cb9ed3..4dd66bc1 100755 --- a/terra-jupyter-minimal-gpu-base/build_docker.sh +++ b/terra-jupyter-minimal-gpu-base/build_docker.sh @@ -1,13 +1,13 @@ #!/bin/bash -base="nvcr.io/nvidia/cuda:11.8.0-base-ubuntu20.04" +base="nvcr.io/nvidia/cuda:12.2.2-base-ubuntu20.04" cp -r ../terra-jupyter-base/custom ../terra-jupyter-minimal-base/custom cp -r ../terra-jupyter-base/scripts ../terra-jupyter-minimal-base/scripts cp -r ../terra-jupyter-base/jupyter_notebook_config.py ../terra-jupyter-minimal-base/jupyter_notebook_config.py docker build \ - -t terra-jupyter-minimal-gpu-base:0.0.2 \ + -t terra-jupyter-minimal-gpu-base:0.0.3 \ -f ../terra-jupyter-minimal-base/Dockerfile \ --build-arg BASE_IMAGE=${base} \ ../terra-jupyter-minimal-base/ From c8537f634ac59c11dc399c67ea99863d9ef94667 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 11:31:26 -0500 Subject: [PATCH 18/26] Fix conda version conflict with libmamba-solver --- terra-jupyter-minimal-base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 401cdd82..cd9f648c 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -17,7 +17,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ # ensure this matches c.NotebookApp.port in jupyter_notebook_config.py JUPYTER_PORT=8000 \ JUPYTER_HOME=/etc/jupyter \ - MINICONDA_VERSION="py310_23.9.0-0" \ + MINICONDA_VERSION="py310_23.5.2-0" \ CONDA_AUTO_UPDATE_CONDA=false \ CONDA_DIR=/opt/conda \ GCLOUD_DIR=/opt/gcloud From e5584eeb34706044e4e25a69ac704eec505daaab Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 12:49:05 -0500 Subject: [PATCH 19/26] Tweak order and versions --- terra-jupyter-minimal-base/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index cd9f648c..efc9eb37 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -74,6 +74,8 @@ RUN useradd -m -s /bin/bash $USER \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ + && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ + && conda config --set solver libmamba \ # Install gsutil with compiled crcmod && mkdir -p $GCLOUD_DIR \ && curl -so $GCLOUD_DIR/google-cloud-cli.tar.gz \ @@ -112,9 +114,7 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba - && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ - && conda config --set solver libmamba \ - && conda install -y -c conda-forge mamba==1.5.0 \ + && conda install -y -c conda-forge mamba==1.5.3 \ && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ # Enable dropdown menu showing all conda envs as jupyter kernels && conda install -c anaconda ipykernel \ From 8fc63f02cadbe46b7cacba14485f1978d8a37d1e Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 14:29:55 -0500 Subject: [PATCH 20/26] Testing why the solver cannot solve the env anymore --- terra-jupyter-minimal-base/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index efc9eb37..1561bb96 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -74,8 +74,8 @@ RUN useradd -m -s /bin/bash $USER \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ - && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ - && conda config --set solver libmamba \ +# && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ +# && conda config --set solver libmamba \ # Install gsutil with compiled crcmod && mkdir -p $GCLOUD_DIR \ && curl -so $GCLOUD_DIR/google-cloud-cli.tar.gz \ @@ -114,8 +114,8 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba - && conda install -y -c conda-forge mamba==1.5.3 \ - && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ +# && conda install -y -c conda-forge mamba==1.5.3 \ +# && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ # Enable dropdown menu showing all conda envs as jupyter kernels && conda install -c anaconda ipykernel \ # Cleanup From 63876540490c7abf2ca77b28a6eca3e81f1b0d96 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 15:04:17 -0500 Subject: [PATCH 21/26] Further testing to identify the issue --- terra-jupyter-minimal-base/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 1561bb96..842768d0 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -74,8 +74,8 @@ RUN useradd -m -s /bin/bash $USER \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ -# && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ -# && conda config --set solver libmamba \ + && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ + && conda config --set solver libmamba \ # Install gsutil with compiled crcmod && mkdir -p $GCLOUD_DIR \ && curl -so $GCLOUD_DIR/google-cloud-cli.tar.gz \ From 663467931da90326599e78c08fa367a5c0a29ff7 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 15:45:13 -0500 Subject: [PATCH 22/26] conda-libmamba-solver was the problem, try to unpin version --- terra-jupyter-minimal-base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 842768d0..daf191ae 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -74,7 +74,7 @@ RUN useradd -m -s /bin/bash $USER \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ - && conda install -n base -c conda-forge conda-libmamba-solver==23.11.0 \ + && conda install -n base -c conda-forge conda-libmamba-solver \ && conda config --set solver libmamba \ # Install gsutil with compiled crcmod && mkdir -p $GCLOUD_DIR \ From dab532be62056c3a06fe9a15b628c0278d01c3ff Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 15:53:37 -0500 Subject: [PATCH 23/26] Resolver found that 23.5.0 works --- terra-jupyter-minimal-base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index daf191ae..8743e75a 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -74,7 +74,7 @@ RUN useradd -m -s /bin/bash $USER \ && chmod +x $HOME/miniconda.sh \ && $HOME/miniconda.sh -b -p $CONDA_DIR \ && rm $HOME/miniconda.sh \ - && conda install -n base -c conda-forge conda-libmamba-solver \ + && conda install -n base -c conda-forge conda-libmamba-solver==23.5.0 \ && conda config --set solver libmamba \ # Install gsutil with compiled crcmod && mkdir -p $GCLOUD_DIR \ From 39282a05534c866ea954ceeba7b793130b930638 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 15:53:58 -0500 Subject: [PATCH 24/26] Bring back mamba --- terra-jupyter-minimal-base/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 8743e75a..89808cb1 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -114,8 +114,8 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba -# && conda install -y -c conda-forge mamba==1.5.3 \ -# && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ + && conda install -y -c conda-forge mamba==1.5.3 \ + && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ # Enable dropdown menu showing all conda envs as jupyter kernels && conda install -c anaconda ipykernel \ # Cleanup From c1119ae2521bb4d56aefa0a77b17d2113449faa9 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 16:11:00 -0500 Subject: [PATCH 25/26] Dependency issues: unpin mamba --- terra-jupyter-minimal-base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 89808cb1..8caa14ff 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -114,7 +114,7 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba - && conda install -y -c conda-forge mamba==1.5.3 \ + && conda install -y -c conda-forge mamba \ && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ # Enable dropdown menu showing all conda envs as jupyter kernels && conda install -c anaconda ipykernel \ From e27c4ca6a94c0e76390acd5cedbcb8f2f1aa7ce7 Mon Sep 17 00:00:00 2001 From: Stephen Fleming Date: Wed, 8 Nov 2023 16:20:23 -0500 Subject: [PATCH 26/26] Pin mamba at a compatible version --- terra-jupyter-minimal-base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terra-jupyter-minimal-base/Dockerfile b/terra-jupyter-minimal-base/Dockerfile index 8caa14ff..68e1f5bf 100644 --- a/terra-jupyter-minimal-base/Dockerfile +++ b/terra-jupyter-minimal-base/Dockerfile @@ -114,7 +114,7 @@ RUN useradd -m -s /bin/bash $USER \ https://raw.githubusercontent.com/broadinstitute/cromwhelm/1ceedf89587cffd355f37401b179001f029f77ed/scripts/workspace_cromwell.py \ && chmod +x /usr/local/bin/workspace_cromwell.py \ # Mamba - && conda install -y -c conda-forge mamba \ + && conda install -y -c conda-forge mamba==1.5.1 \ && conda install -y -c conda-forge libarchive==3.6.2 requests==2.31.0 \ # Enable dropdown menu showing all conda envs as jupyter kernels && conda install -c anaconda ipykernel \