From d5b5ce8aa3c3ced9d7fe8f21a98a80bd72732f21 Mon Sep 17 00:00:00 2001 From: justinjao Date: Sun, 10 Sep 2023 16:21:29 -0700 Subject: [PATCH 1/4] Add infrastructure for preview deployments --- preview/cbioportal-docker-compose/.env | 3 + .../docker-compose.yml | 63 +++++++++++++++++++ .../cbioportal-docker-compose/study/.gitkeep | 0 3 files changed, 66 insertions(+) create mode 100644 preview/cbioportal-docker-compose/.env create mode 100644 preview/cbioportal-docker-compose/docker-compose.yml create mode 100644 preview/cbioportal-docker-compose/study/.gitkeep diff --git a/preview/cbioportal-docker-compose/.env b/preview/cbioportal-docker-compose/.env new file mode 100644 index 0000000000..e507f195ee --- /dev/null +++ b/preview/cbioportal-docker-compose/.env @@ -0,0 +1,3 @@ +DOCKER_IMAGE_CBIOPORTAL=registry.cloud.okteto.net/justinjao/cbioportal-docker-compose-cbioportal:okteto-with-volume-mounts +DOCKER_IMAGE_SESSION_SERVICE=cbioportal/session-service:0.6.1 +DOCKER_IMAGE_MYSQL=registry.cloud.okteto.net/justinjao/cbioportal-docker-compose-cbioportal-database:okteto-with-volume-mounts \ No newline at end of file diff --git a/preview/cbioportal-docker-compose/docker-compose.yml b/preview/cbioportal-docker-compose/docker-compose.yml new file mode 100644 index 0000000000..9187c318cb --- /dev/null +++ b/preview/cbioportal-docker-compose/docker-compose.yml @@ -0,0 +1,63 @@ +version: '3' + +services: + cbioportal: + restart: unless-stopped + image: ${DOCKER_IMAGE_CBIOPORTAL} + container_name: cbioportal-container + environment: + SHOW_DEBUG_INFO: "true" + ports: + - "8080:8080" + volumes: + - ./study:/study/ + - ./config/portal.properties:/cbioportal/portal.properties + depends_on: + - cbioportal-database + - cbioportal-session + networks: + - cbio-net + command: /bin/sh -c "java -Xms2g -Xmx4g -Dauthenticate=noauthsessionservice -Dsession.service.url=http://cbioportal-session:5000/api/sessions/my_portal/ -jar webapp-runner.jar -AmaxHttpHeaderSize=16384 -AconnectionTimeout=20000 --enable-compression /cbioportal-webapp" + cbioportal-database: + restart: unless-stopped + image: ${DOCKER_IMAGE_MYSQL} + container_name: cbioportal-database-container + environment: + MYSQL_DATABASE: cbioportal + MYSQL_USER: cbio_user + MYSQL_PASSWORD: somepassword + MYSQL_ROOT_PASSWORD: somepassword + volumes: + - ./data/cgds.sql:/docker-entrypoint-initdb.d/cgds.sql + - ./data/seed.sql.gz:/docker-entrypoint-initdb.d/seed.sql.gz + - cbioportal_mysql_data:/var/lib/mysql + networks: + - cbio-net + cbioportal-session: + restart: unless-stopped + image: ${DOCKER_IMAGE_SESSION_SERVICE} + container_name: cbioportal-session-container + environment: + SERVER_PORT: 5000 + JAVA_OPTS: -Dspring.data.mongodb.uri=mongodb://cbioportal-session-database:27017/session-service + depends_on: + - cbioportal-session-database + networks: + - cbio-net + cbioportal-session-database: + restart: unless-stopped + image: mongo:4.2 + container_name: cbioportal-session-database-container + environment: + MONGO_INITDB_DATABASE: session_service + volumes: + - cbioportal_mongo_data:/data/db + networks: + - cbio-net + +networks: + cbio-net: + +volumes: + cbioportal_mysql_data: + cbioportal_mongo_data: \ No newline at end of file diff --git a/preview/cbioportal-docker-compose/study/.gitkeep b/preview/cbioportal-docker-compose/study/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 From 3e0087febad5764db9d26e58c21f3221d4754615 Mon Sep 17 00:00:00 2001 From: justinjao Date: Sun, 10 Sep 2023 16:32:22 -0700 Subject: [PATCH 2/4] Add workflow files --- .github/workflows/preview.yml | 105 +++++++++++++++++++++++++++ .github/workflows/preview_closed.yml | 19 +++++ 2 files changed, 124 insertions(+) create mode 100644 .github/workflows/preview.yml create mode 100644 .github/workflows/preview_closed.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000000..bab8ffb0ee --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,105 @@ +on: + pull_request: + branches: + - "*" + +jobs: + changed_files: + runs-on: ubuntu-latest + outputs: + new_study_dirs: ${{ steps.new-dirs.outputs.NEW_DIR_LOCATIONS }} + steps: + - name: Get New Directories Added + id: changed-files-dir-names + uses: tj-actions/changed-files@v38 + with: + dir_names: "true" + + - name: Extract Parent Directory Names of New Studies + id: new-dirs + shell: bash + run: | + echo "NEW_DIR_LOCATIONS=$( echo ${{ steps.changed-files-dir-names.outputs.added_files }} | sed 's/[^ ]*\/case_lists[^ ]*//g' )" >> $GITHUB_OUTPUT + + preview: + needs: changed_files + runs-on: ubuntu-latest + container: docker.io/okteto/okteto:2.19.1 + steps: + - name: Install Git LFS + run: apk update && apk add git-lfs + + - name: Split Directories Into New Line Pattern For Sparse Checkout + id: split + run: | + echo "STUDY_DIRS<> $GITHUB_ENV + echo "$( echo ${{ needs.changed_files.outputs.new_study_dirs }} | tr ' ' '\n')" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Checkout Datahub Repository + uses: actions/checkout@v4 + with: + lfs: true + sparse-checkout-cone-mode: false + sparse-checkout: | + preview/* + ${{ env.STUDY_DIRS }} + + - name: Copy New Files to Study Directory + shell: bash + run: | + for dir in ${{ needs.changed_files.outputs.new_study_dirs }}; do + study_name=$( cut -d "/" -f2- <<< ${dir}) # remove 'public/' from string + cp -v -R ${dir} preview/cbioportal-docker-compose/study/${study_name} + done + + - name: Context + uses: okteto/context@latest + with: + url: ${{secrets.OKTETO_URL}} + token: ${{ secrets.OKTETO_TOKEN }} + + - name: Okteto Build to Import Studies + working-directory: preview/cbioportal-docker-compose + run: | + okteto build --no-cache -t okteto.dev/cbioportal-docker-compose-cbioportal:okteto-with-volume-mounts cbioportal + + - name: Deploy Preview Environment + uses: okteto/deploy-preview@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: pr-${{ github.event.number }}-justinjao + file: preview/cbioportal-docker-compose/docker-compose.yml + timeout: 15m + + - name: Wait For Response From Preview Instance + uses: nev7n/wait_for_response@v1.0.1 + with: + url: 'https://cbioportal-pr-${{ github.event.number }}-justinjao.cloud.okteto.net/' + responseCode: 200 + timeout: 600000 # 10 minutes + interval: 30000 # 30 seconds + + - name: Activate Namespace + uses: okteto/namespace@latest + with: + namespace: pr-${{github.event.number}}-justinjao + + - name: Run Metaimport Script to Import Study Using Kubectl + id: import-study + continue-on-error: true + shell: bash + run: | + okteto kubeconfig + for dir in ${{ needs.changed_files.outputs.new_study_dirs }}; do + study_name=$( cut -d "/" -f2- <<< ${dir}) + kubectl exec -it deployment/cbioportal -- metaImport.py -u http://localhost:8080 -s study/${study_name} -o + done + + - name: Add PR Comment if Import Failed + uses: mainmatter/continue-on-error-comment@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + outcome: ${{ steps.import-study.outcome }} + test-id: Error code 1 \ No newline at end of file diff --git a/.github/workflows/preview_closed.yml b/.github/workflows/preview_closed.yml new file mode 100644 index 0000000000..871bc0fc08 --- /dev/null +++ b/.github/workflows/preview_closed.yml @@ -0,0 +1,19 @@ +on: + pull_request: + types: + - closed + +jobs: + destroy-pr-env: + runs-on: ubuntu-latest + steps: + - name: Context + uses: okteto/context@latest + with: + token: ${{ secrets.OKTETO_TOKEN }} + url: ${{ secrets.OKTETO_URL }} + + - name: Destroy Preview Environment + uses: okteto/destroy-preview@latest + with: + name: pr-${{ github.event.number }}-justinjao \ No newline at end of file From bc8bf2a491c13c8bc6bba4e3578315fc9e1dcbb3 Mon Sep 17 00:00:00 2001 From: justinjao Date: Sun, 10 Sep 2023 21:07:07 -0700 Subject: [PATCH 3/4] Add documentation files --- docs/Preview_Overview.md | 74 ++++++++++++++++++++++++++++ docs/Preview_Setup_&_Maintenance.md | 51 +++++++++++++++++++ docs/images/Intial_Setup.svg | 4 ++ docs/images/Preview2.svg | 4 ++ docs/images/gene_panel_error.png | Bin 0 -> 17861 bytes 5 files changed, 133 insertions(+) create mode 100644 docs/Preview_Overview.md create mode 100644 docs/Preview_Setup_&_Maintenance.md create mode 100644 docs/images/Intial_Setup.svg create mode 100644 docs/images/Preview2.svg create mode 100644 docs/images/gene_panel_error.png diff --git a/docs/Preview_Overview.md b/docs/Preview_Overview.md new file mode 100644 index 0000000000..cbc0159194 --- /dev/null +++ b/docs/Preview_Overview.md @@ -0,0 +1,74 @@ +# Preview Environment Project Documentation + +To streamline the quality control process of reviewing external data sources, cBioPortal seeks to automate the deployment of a live staging instance with the new studies already imported. The Preview Environment workflow utilizes a combination of infrastructure-as-code tooling (`docker-compose`, `GitHub Actions`), and a fully-managed developer environment provisioned by Okteto (which runs via a `GitHub Action`). + +The specification for building the containerized web applications is primarily accomplished through the configuration provided by the [`cbioportal-docker-compose`](https://github.com/cBioPortal/cbioportal-docker-compose) repository. + +Okteto can appropriately parse the `docker-compose.yml` file, but to use these with Okteto to deploy a working staging instance of cBioPortal requires a few additional things to be set up in the infrastructure: + +* configuration files for the cbioportal instance +* initial files and SQL commands to seed the initial SQL database +* have these files already present in an accessible image (for reasons outlined below). + +These requirements are thus achieved through an initial setup procedure, followed by 2 workflow files, `preview.yml`, which deploys the staging environment with the study imported, and `preview_closed.yml`, which tears down the deployed instance. + +The following document outlines each of the steps of the workflow, and provides insight where necessary as to why they were designed in such a manner. + +### Initial Setup + +![image](./Intial_Setup.svg) + +The [`cbioportal-docker-compose`](https://github.com/cBioPortal/cbioportal-docker-compose) repository contains the files necessary to build a fully running instance of `cbioportal`. However, to get this to run, and to allow Okteto to access this in their `Deploy Preview` action, certain configuration files (such as the `portal.properties` document), and data to seed the SQL database, must be present. + +The `cbioportal-docker-compose` repository contains a script, `init.sh`, that initializes these files. After checking out the repository, this script can be executed, and an `okteto build` command can be run to push the image. + +This step is thus performed for 2 reasons: to initialize the namespace in the private Okteto registry, as well as to push an image with the configuration files and seeded DB files already in the image. + +For a more detailed breakdown of the steps necessary here, as well as other prerequisites to getting this setup, see [Preview Setup](Preview_Setup.md). + +(AVERY: should I reference the setup doc? Or is it certain that will be internal facing, and this might be public facing?) + +### Workflow Overview + +In brief, the proposed workflow works by pulling the previously described pre-built images of cBioPortal (with the configuration files added) and the cBioPortal SQL database (with the database files for seeding already added as well). These images are used to launch a container with the new studies imported as well, and this container is re-built to the same namespace registry. The `Deploy Preview` action by Okteto then pulls this image and uses it to deploy the preview environment. Upon which, a `metaImport.py` script can be run to import the study into the running application instance. + +![Preview](./Preview2.svg) + +### Identify and Get New Files (2) +The datahub repo is very large, and most files are stored using [Git LFS](https://git-lfs.com/), which by default doesn't store the actual file content in the repository (rather, they are references to the data, which is stored elsewhere). However, when importing an actual study, we need the actual file data. As a result, it is not possible to check out the entire repository's files during a GitHub Action workflow (the runner runs out of space). We therefore need to know which exact studies are the newly added ones that we want to preview, and checkout only these specific files from the repository. This also has the added benefit of making the review process for previewing the studies easier, as only new studies are imported into the staging instance. + +The workflow uses a combination of various actions to determine the new directories added, and some text processing to convert them to strings in the proper format for input to the `checkout` action step. It is then checked out using Git's [sparse checkout](https://git-scm.com/docs/git-sparse-checkout) feature. + +### Rebuilding cbioportal Image and Pulling Images (3) +Within the `datahub` repo, there is a `cbioportal-docker-compose` directory (nested under `preview`) containing the infrastructure needed to deploy to Okteto. Specifically, there is: +* a `docker-compose.yml` file, nearly identical to the one within the `cbioportal-docker-compose` repo, apart from a minor syntax change +* a .env file specifying the locations to pull the images from during the deploy process +* an empty `study` directory where new studies will be copied to + +Once the new studies have been determined, they are transferred to this `study` directory. The `cbioportal` image is then re-built and pushed to the Okteto registry using an `okteto build` command (pushing to the exact registry defined in the .env file for `cbioportal`). As part of this process, all the files within the `study` directory are mounted into the re-built cbioportal container, which is how the studies are made available to the Okteto instance. + + +### Deploy Containers to Preview Environment (4) +Okteto's `Deploy Preview` action checks out the datahub repository and builds the PR environment from a clean clone. This is the primary reason why the previous image build steps were necessary (to both determine which study to be imported, as well as prevent cloning every other study). + +The built images (the pre-built `cbioportal-database` one from the setup, and the newly built `cbioportal`), which are specified in the `.env` file, are then pulled from the Okteto registry and used in the subsequent `Deploy Preview` action by Okteto. The `docker.compose.yml` is read by the `Deploy Preview` action, and the deploy process is started. + +> :bulb: The GitHub Action for the deploy process actually finishes prematurely -- even after the service deploys on Okteto, it takes a while for the database to finish initializing. In between this time when the deploy action finishes, and the database is initializing, the public URL returns a 502 error. Therefore, an additional action step, **Wait For Response** is added to ping the URL until the service is fully functional, before proceeding. + +One the deploy has fully finished, a message is posted on the PR with the public URL available to visit. At this step, a live staging instance is now deployed, and the studies are present in a `study` directory within the `cbioportal` container. However, the new studies have yet to be imported. + +### Import Study (5) + +The `kubectl` command is used to run a `metaImport.py` script (which is also located within the `cbioportal` container). This imports the data the database, at which point the data will then be visible on the staging instance. This is setup to be able to import multiple studies. + +Note that currently, the setup initialization seeds the database with certain gene panel IDs. However, if a new study is imported containing IDs not already existing within the database, the `metaImport.py` script will fail. +![error](./gene_panel_error.png) + +As such, this step is setup to always pass within the workflow for now, until this issue can be addressed in a future update to the workflow. + +> :bulb: To prevent users from missing the error, an additional action step, **Add PR Comment If Import Fails** is added, to notify users if this step fails. + + +### Teardown of Preview Environment (6 & 7) + +Finally, when a PR is either closed or merged, a `preview_closed.yml` workflow is run, which automatically tears down the deployed environment. This is currently necessary due to cost saving measures, as well as the fact that on the free tier, we are limited to roughly 2-3 open Preview Environments at any point in time. diff --git a/docs/Preview_Setup_&_Maintenance.md b/docs/Preview_Setup_&_Maintenance.md new file mode 100644 index 0000000000..26476c4e6c --- /dev/null +++ b/docs/Preview_Setup_&_Maintenance.md @@ -0,0 +1,51 @@ +# Okteto Developer Setup for cBioPortal Maintainers + +This documentation serves as an outline for the exact work performed to setup the Okteto preview pipeline. If this were ever to be set up again from scratch, following these steps should replicate the existing infrastructure. For more insight as to why things were developed in this manner, refer to [Preview Overview](Preview_Overview.md). + +## One time Setup (If re-building from scratch): + +1. Okteto Account Creation - Visit [Here](https://www.okteto.com/try-free/) and link a GitHub account. Note that the account must be linked to a business email (i.e. non-gmail account). +2. Install the Okteto CLI and initialize the context as per instructions [here](https://www.okteto.com/docs/getting-started/#installing-okteto-cli). +3. Generate Okteto Personal Access Token (PAT) as per instructions [here](https://www.okteto.com/docs/cloud/personal-access-tokens/) +4. Clone the `cbioportal-docker-compose` repository and run the initialization script, `./init.sh` in the root directory. +5. Remove all instances of the `:ro` text within the `docker-compose.yml` file, as this is not supported by Okteto. +6. Run `okteto context use https://cloud.okteto.com`, to initialize the namespace Okteto will use. +7. Run `okteto build` to build and push an initial *cbioportal* and *cbioportal-database* image to the Okteto Registry with the initialized configuration present. + +Within the Datahub repo: + +8. Add new secret variable OKTETO_TOKEN using the PAT. +9. Add new secret variable OKTETO_URL (https://cloud.okteto.com). +10. In the repo settings > Code and automation > Actions > General > Workflow Permissions, enable workflow read-write permissions to allow Okteto to post messages in a PR. +11. Add new workflow file (`preview.yml`) from PR to .github folder of datahub repository. +12. Add the following files to setup infrastructure for re-building image in PR. Specifically: + + a. Run `mkdir -p preview/cbioportal-docker-compose/study` to create the necessary directories + + b. Add a `.gitkeep` file within the `study` directory to ensure git keeps track of an empty repository + + c. Within the `preview/cbioportal-docker-compose` directory, add a modified `docker-compose.yml`, from the file in Step 5 (the file must have all instances of the string `:ro`, removed as Okteto does not support this syntax). + + d. Within the `preview/cbioportal-docker-compose` directory, add a `.env` file containing the images to be built, with the link pointing to the Okteto registry: + + DOCKER_IMAGE_CBIOPORTAL=registry.cloud.okteto.net//cbioportal-docker-compose-cbioportal:okteto-with-volume-mounts + + DOCKER_IMAGE_MYSQL=registry.cloud.okteto.net//cbioportal-docker-compose-cbioportal-database:okteto-with-volume-mounts + +Following this setup, subsequent PRs should correctly trigger a staging environment with the new studies imported. + +## Maintenance of Current Setup + +### Rebuilding the Image to Update cBioPortal Version +Once the workflow has been setup, the private registry has already been initialized. The .env file specifies the private registry, and as such, the deploy preview environment pulls from the previously built images. One thing to note however is that currently, the build version of cbioportal is locked to whatever the most up to date version is during the initial setup where the image was built and pushed from `cbioportal-docker-compose`. Therefore, there may come a point when the version of cBioPortal is too out of date and must be updated. + +Essentially, steps 4-7 must be rerun to update the build image. + +To simplify this process, a `preview_init.sh` script has been created to replicate this process, and should be executable provided Okteto and git has been correctly set up on the running machine. + + +### Changing of Namespace +If the Okteto account were ever to be migrated to a different namespace, it should be sufficient to simply re-run steps 4-7, and rename all instances of the namespace to the current desired user (e.g. justinjao) in the `preview.yml`, `preview_closed.yml`, and `.env` files. + +### Note About Directory Structure +The namespace of the directory is important, as this is how the `okteto build` command determines what namespace to push the service to (i.e. if the directory is called `cbioportal-docker-compose`, it would push to `okteto.dev/cbioportal-docker-compose-cbioportal:okteto-with-volume-mounts`). This is why within Datahub, while it may not make much sense to have a directory named `cbioportal-docker-compose`, there exists such a specific directory nested under `preview`. The directory could have been changed, which would result in the build image tag name differing. However, the thinking here was that maintaining this namespeace would be easiest for maintenance, as this allows steps 4-7 to easily be automated into the `preview_init.sh` script to rebuild the image via a clean clone of the `cbioportal-docker-compose` repo, without any other change. \ No newline at end of file diff --git a/docs/images/Intial_Setup.svg b/docs/images/Intial_Setup.svg new file mode 100644 index 0000000000..66f2d6f964 --- /dev/null +++ b/docs/images/Intial_Setup.svg @@ -0,0 +1,4 @@ + + + +
Private Okteto Registry




















Private Okteto Registry...
Run init.sh script
to generate config and 
database seeding
Run init.sh script...
cbioportal-docker-compose repo
cbioportal-docker-co...
cbioportal-docker-compose-cbioportal Image
cbioportal-docker-compose-cb...
cbioportal

portal.properties
cbioportal...
cbioportal-docker-compose-cbioportal-database Image
cbioportal-docker-compose-cb...
Initial seeded SQL Database
Initial see...
Run okteto build
to containerize and push
images with configuration and
seeded database to 
registry
Run okteto build...
cbioportal-docker-compose repo

portal.properties

data files to seed SQL database
cbioportal-docker-compos...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/images/Preview2.svg b/docs/images/Preview2.svg new file mode 100644 index 0000000000..784731193d --- /dev/null +++ b/docs/images/Preview2.svg @@ -0,0 +1,4 @@ + + + +


Private Okteto Registry













Private Okteto Registry...
Identify newly
added study
files for import
Identify newly...
PR is closed
or merged
PR is closed...
Triggers automated teardown
of preview environment
Triggers automated teardown...
Datahub
Repository
Datahub...
Image is Pulled
Image is Pulled
cbioportal-docker-compose-cbioportal Image
cbioportal-docker-compose-cb...
cbioportal

portal.properties
cbioportal...
User uploads data
via Pull Request and
triggers workflow
User uploads data...
New
Dataset
New...
Deploys preview environment
using docker-compose.yml
and imports study
Deploys preview environment...
docker-compose.yml
+ Okteto
docker-compose.yml...
Image is 
Pulled
Image is...
cbioportal-docker-compose-cbioportal-database Image
cbioportal-docker-compose-cb...
Initial seeded SQL Database
Initial see...
Run metaImport.py
using Kubectl
Run metaImport.py...
Okteto Preview Environment                                  














Okteto Preview Environment...
cbioportal
portal.properties


cbioportal...
cbioportal-session
cbioportal-session
cbioportal-session-database (MongoDB)
cbioportal-session-d...
seeded cbioportal-database (SQL)


seeded cbiopo...
1
1
3
3
3
3
2
2
New
Dataset
New...
Okteto Preview Environment                                  














Okteto Preview Environment...
cbioportal
portal.properties


cbioportal...
cbioportal-session
cbioportal-session
cbioportal-session-database (MongoDB)
cbioportal-session-d...
seeded cbioportal-database (SQL)


seeded cbiopo...
New
Dataset
New...
4
4
5
5
preview.yml
preview.yml
preview_closed.yml
preview_closed.y...
*(study not in DB)
*(study not in DB)
*(study populated in DB)
*(study populated in DB)
6
6
7
7
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/images/gene_panel_error.png b/docs/images/gene_panel_error.png new file mode 100644 index 0000000000000000000000000000000000000000..c6aee9efde91650963a05bdacf3971ab3a1b799a GIT binary patch literal 17861 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 literal 0 HcmV?d00001 From bbb4d390d7fca53b587fccedd199794725d4ef9f Mon Sep 17 00:00:00 2001 From: justinjao Date: Sun, 10 Sep 2023 22:28:12 -0700 Subject: [PATCH 4/4] update images --- docs/Preview_Overview.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/Preview_Overview.md b/docs/Preview_Overview.md index cbc0159194..21061c5e2a 100644 --- a/docs/Preview_Overview.md +++ b/docs/Preview_Overview.md @@ -16,7 +16,7 @@ The following document outlines each of the steps of the workflow, and provides ### Initial Setup -![image](./Intial_Setup.svg) +![image](images/Intial_Setup.svg) The [`cbioportal-docker-compose`](https://github.com/cBioPortal/cbioportal-docker-compose) repository contains the files necessary to build a fully running instance of `cbioportal`. However, to get this to run, and to allow Okteto to access this in their `Deploy Preview` action, certain configuration files (such as the `portal.properties` document), and data to seed the SQL database, must be present. @@ -26,13 +26,11 @@ This step is thus performed for 2 reasons: to initialize the namespace in the pr For a more detailed breakdown of the steps necessary here, as well as other prerequisites to getting this setup, see [Preview Setup](Preview_Setup.md). -(AVERY: should I reference the setup doc? Or is it certain that will be internal facing, and this might be public facing?) - ### Workflow Overview In brief, the proposed workflow works by pulling the previously described pre-built images of cBioPortal (with the configuration files added) and the cBioPortal SQL database (with the database files for seeding already added as well). These images are used to launch a container with the new studies imported as well, and this container is re-built to the same namespace registry. The `Deploy Preview` action by Okteto then pulls this image and uses it to deploy the preview environment. Upon which, a `metaImport.py` script can be run to import the study into the running application instance. -![Preview](./Preview2.svg) +![Preview](images/Preview2.svg) ### Identify and Get New Files (2) The datahub repo is very large, and most files are stored using [Git LFS](https://git-lfs.com/), which by default doesn't store the actual file content in the repository (rather, they are references to the data, which is stored elsewhere). However, when importing an actual study, we need the actual file data. As a result, it is not possible to check out the entire repository's files during a GitHub Action workflow (the runner runs out of space). We therefore need to know which exact studies are the newly added ones that we want to preview, and checkout only these specific files from the repository. This also has the added benefit of making the review process for previewing the studies easier, as only new studies are imported into the staging instance. @@ -62,7 +60,8 @@ One the deploy has fully finished, a message is posted on the PR with the public The `kubectl` command is used to run a `metaImport.py` script (which is also located within the `cbioportal` container). This imports the data the database, at which point the data will then be visible on the staging instance. This is setup to be able to import multiple studies. Note that currently, the setup initialization seeds the database with certain gene panel IDs. However, if a new study is imported containing IDs not already existing within the database, the `metaImport.py` script will fail. -![error](./gene_panel_error.png) + +![error](images/gene_panel_error.png) As such, this step is setup to always pass within the workflow for now, until this issue can be addressed in a future update to the workflow.