diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml index 76e8788819f6139821b2794a5452ae25ee55d7fe..b6bef4b40b4888106c797d746175c6d3dd465005 100644 --- a/.github/workflows/docker_img.yml +++ b/.github/workflows/docker_img.yml @@ -6,16 +6,17 @@ jobs: build-docker-img: runs-on: ubuntu-latest env: - MATOMO_ID_DEV: '7' - MATOMO_URL_DEV: 'https://stats-dev.humanbrainproject.eu/' MATOMO_ID_PROD: '12' MATOMO_URL_PROD: 'https://stats.humanbrainproject.eu/' PRODUCTION: 'true' DOCKER_REGISTRY: 'docker-registry.ebrains.eu/siibra/' + SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v1_0' SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v1_0' SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v1_0' + OC_TEMPLATE_NAME: 'siibra-explorer-branch-deploy-2' + steps: - uses: actions/checkout@v2 - name: 'Set matomo env var' @@ -57,6 +58,7 @@ jobs: VERSION=$(git rev-parse --short HEAD) echo "EXPERIMENTAL_FEATURE_FLAG=true" >> $GITHUB_ENV fi + echo "Setting VERSION: $VERSION" echo "VERSION=$VERSION" >> $GITHUB_ENV - name: 'Build docker image' run: | @@ -112,13 +114,13 @@ jobs: echo "OKD_URL=https://okd.hbp.eu:443" >> $GITHUB_ENV echo "OKD_SECRET=${{ secrets.OKD_PROD_SECRET }}" >> $GITHUB_ENV echo "OKD_PROJECT=interactive-viewer" >> $GITHUB_ENV - echo "PATH_POSTFIX=" >> $GITHUB_ENV + echo "ROUTE_HOST=https://siibra-explorer.apps.hbp.eu" >> $GITHUB_ENV echo "Deploy on prod cluster..." else echo "OKD_URL=https://okd-dev.hbp.eu:443" >> $GITHUB_ENV echo "OKD_SECRET=${{ secrets.OKD_DEV_SECRET }}" >> $GITHUB_ENV echo "OKD_PROJECT=interactive-atlas-viewer" >> $GITHUB_ENV - echo "PATH_POSTFIX=-dev" >> $GITHUB_ENV + echo "ROUTE_HOST=https://siibra-explorer.apps-dev.hbp.eu" >> $GITHUB_ENV echo "BUILD_TEXT=$BRANCH_NAME" >> $GITHUB_ENV echo "Deploy on dev cluster..." fi @@ -127,39 +129,36 @@ jobs: oc login $OKD_URL --token=$OKD_SECRET oc project $OKD_PROJECT - # sanitized branchname == remove _ / and lowercase everything - SANITIZED_BRANCH_NAME=$(echo ${BRANCH_NAME//[_\/]/} | awk '{ print tolower($0) }') - echo "SANITIZED_BRANCH_NAME=$SANITIZED_BRANCH_NAME" >> $GITHUB_ENV - echo "Working branch name: $BRANCH_NAME, sanitized branch name: $SANITIZED_BRANCH_NAME" + # DEPLOY_ID == remove _ / and lowercase everything from branch + DEPLOY_ID=$(echo ${BRANCH_NAME//[_\/]/} | awk '{ print tolower($0) }') + echo "DEPLOY_ID=$DEPLOY_ID" >> $GITHUB_ENV + + ROUTE_PATH=$DEPLOY_ID + echo "ROUTE_PATH=$ROUTE_PATH" >> $GITHUB_ENV + + echo "Working branch name: $BRANCH_NAME, deploy_id: $DEPLOY_ID" # check if the deploy already exist - if oc get dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME; then + if oc get dc ${{ env.OC_TEMPLATE_NAME }}-$DEPLOY_ID; then # trigger redeploy if deployconfig exists already - echo "dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME already exist, redeploy..." - oc rollout latest dc/siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME + echo "dc ${{ env.OC_TEMPLATE_NAME }}-$DEPLOY_ID already exist, redeploy..." + oc rollout latest dc/${{ env.OC_TEMPLATE_NAME }}-$DEPLOY_ID else # create new app if deployconfig does not yet exist - echo "dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME does not yet exist, create new app..." - oc new-app --template siibra-explorer-branch-deploy \ + echo "dc ${{ env.OC_TEMPLATE_NAME }}-$DEPLOY_ID does not yet exist, create new app..." + oc new-app --template ${{ env.OC_TEMPLATE_NAME }} \ -p BRANCH_NAME=$BRANCH_NAME \ - -p SANITIZED_BRANCH_NAME=$SANITIZED_BRANCH_NAME \ + -p DEPLOY_ID=$DEPLOY_ID \ -p PATH_POSTFIX=$PATH_POSTFIX \ + -p ROUTE_HOST=$ROUTE_HOST \ + -p ROUTE_PATH=$DEPLOY_ID \ -p BUILD_TEXT=$BUILD_TEXT fi - name: 'Update status badge' if: success() run: | - if [[ "$GITHUB_REF" == 'refs/heads/master' ]] - then - DEPLOY_URL="https://siibra-explorer.apps.hbp.eu/master" - elif [[ "$GITHUB_REF" == 'refs/heads/staging' ]] - then - DEPLOY_URL="https://siibra-explorer.apps.hbp.eu/staging" - else - DEPLOY_URL="https://siibra-explorer.apps-dev.hbp.eu/${{ env.SANITIZED_BRANCH_NAME }}" - fi - + DEPLOY_URL=$ROUTE_HOST$ROUTE_PATH curl -v \ -X POST \ -H "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ diff --git a/.openshift/README.md b/.openshift/v1/README.v1.md similarity index 98% rename from .openshift/README.md rename to .openshift/v1/README.v1.md index db1f804c7db0affbedcc01c271f015893c51fa20..7be9e08e08289363b9cb34c5fcf46f5769b8cc51 100644 --- a/.openshift/README.md +++ b/.openshift/v1/README.v1.md @@ -143,7 +143,6 @@ Per [deployment template](./okd-branch-tmpl.yaml), a number of parameters may be | name | required | desc | | --- | --- | --- | | `SESSION_SECRET` | | Random strings to encrypt sessions. Not currently used. | -| `DOCKER_IMAGE_TAG` | true | Dictates which image tag to pull. Currently, possible values are `{latest\|rc\|stable}`. | | `BRANCH_NAME` | true | Determines the tag of the image to pull. | | `SANITIZED_BRANCH_NAME` | true | Strip all special characters from `BRANCH_NAME`. Acts similar to deploy ID. Distinguishes one deployment from another. Also affects routes: `siibra-explorer-{SANITIZED_BRANCH_NAME}.apps{PATH_POSTFIX}.hbp.eu` | | `PATH_POSTFIX` | | Dictates if postfix, if any, should be added to the route: `siibra-explorer-{DEPLOY_FLAVOUR}.apps{PATH_POSTFIX}.hbp.eu`. Defaults to `''` (empty string). Possible value: `-dev`| diff --git a/.openshift/okd_branch_tmpl.yaml b/.openshift/v1/okd_branch_tmpl_v1.yaml similarity index 100% rename from .openshift/okd_branch_tmpl.yaml rename to .openshift/v1/okd_branch_tmpl_v1.yaml diff --git a/.openshift/v2/README.v2.md b/.openshift/v2/README.v2.md new file mode 100644 index 0000000000000000000000000000000000000000..902bfd83fd171eff1881e8bde58849dca4c8a470 --- /dev/null +++ b/.openshift/v2/README.v2.md @@ -0,0 +1,150 @@ +# Deployment documentation + +This document outlines the deployment of `siibra-explorer` on EBRAINS infrastructure. + +## Overview + +`siibra-explorer` are continuously deployed on openshift container platform hosted by EBRAINS. + +The continuous deployment of `siibra-explorer` involves the following steps: + +- building docker image +- tag the image, and push to registry +- pull and run the newly built image + +## Build + +This section outlines the procedure of continuously building and archiving docker images of `siibra-explorer`. + +### Images + +Docker images are built with [`Dockerfile`](../Dockerfile) by github action withs [yml spec](../.github/workflows/docker_img.yml), and pushed to EBRAINS docker image registry at `docker-registry.ebrains.eu` + +`docker-registry.ebrains.eu` is set as the registry + +`siibra` is set as the namespace + +`siibra-explorer` is set as the image name + +The built image will be tagged with the branch name. e.g. + +`docker-registry.ebrains.eu/siibra/siibra-explorer:{BRANCH_NAME}` + +### Registry + +The built docker image will then be pushed to `docker-registry.ebrains.eu` with the access token of a bot account with the rights to push image in `siibra` namespace. + +The login credentials are stored in github action secrets: + +- username: `{{ secrets.EBRAINS_DOCKER_REG_USER }}` +- access token: `{{ secrets.EBRAINS_DOCKER_REG_TOKEN }}` + +> :warning: There are currently no mechanism to delete artefacts from `docker-registry.ebrains.eu`. One must periodically, manually delete untagged images to avoid filling of allotted diskspace. + +--- + +// TODO setup retention policy to allow automatic deletion of artefacts + +--- + +## Deployment + +This section outlines how the built image are deployed. + +> :information_source: Previous internal guides described a combination of s2i with docker build strategy. This has been demonstrated to be both slow (at build time) and unreliable (over the deployment lifetime). + +### Variables + +| cluster | name | value | +| --- | --- | --- | +| prod | `PROJECT_NAME` | `interactive-viewer` | +| | `OKD_ENDPOINT` | `https://okd.hbp.eu:443` | +| | `OKD_SECRET` | `{{ secrets.OKD_PROD_SECRET }}` (generated once[1] stored in github action secrets) | +| dev | `PROJECT_NAME` | `interactive-viewer` | +| | `OKD_ENDPOINT` | `https://okd-dev.hbp.eu:443` | +| | `OKD_SECRET` | `{{ secrets.OKD_DEV_SECRET }}` (generated once[1], stored in github action secrets) | + + +### Triggering deployment + +Deployments resides in [docker-img.yml](../.github/workflows/docker_img.yml), `job['trigger-deploy']`. The steps are summarised as below: + +- determine if targeting prod or dev cluster. + + - If the trigger is update of `master` or `staging` branch, target prod cluster + + - Anyother branch, target dev cluster + +- login to openshift container platform with the command + + ```bash + oc login ${OKD_ENDPOINT} --token ${OKD_SECRET} + ``` + +- checkout project with the command + + ```bash + oc project ${PROJECT_NAME} + ``` + +- check if deployment with name `siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}` exists + - if exists, rollout latest deployment with the command + + ```bash + oc rollout latest dc/siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME} + ``` + + - if does not exist, create new deployment with name `siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}`, using deployment template[2] with corresponding parameters[3] + +### [1] OKD service accounts + +In order to deploy on OKD clusters in CI/CD pipeline, it is ideal to create a service account. Openshift container platform maintains [a comprehensive guide](https://docs.openshift.com/container-platform/3.11/dev_guide/service_accounts.html) on service account. This section provides a step by step guide on creating the service account. + +> :information_source: Why not just use personal access token? 1/ it expires, 2/ it is invalidated when you logout, 3/ (to a less degree, since personal access token has a expiration), revoking personal access token has a greater impact on developer experience, and potentially breaks more things, if one reuses the same personally access token. + +#### Prereq + +- openshift cli installed (check via `which oc`) +- login command (easiest method to obtain login command: login via web portal > portrait username (*top right*) > Copy Login Command ) + +#### Configure a Service Account + +- login via terminal (paste login command from prereq) +- select the desired project via `oc project ${PROJECT_NAME}` +- create a new SA via: `oc create sa ${SERVICE_ACCOUNT_NAME}` +- get a new token via: `oc sa get-token ${SERVICE_ACCOUNT_NAME}` (store this token securely, ideally in a password manager) +- grant the SA ability to create deployments via: `oc policy add-role-to-user edit -z ${SERVICE_ACCOUNT_NAME}` + + +### [2] Deployment template + +An [openshift template](./okd-branch-tmpl.yaml) has been added to both production (https://okd.hbp.eu) and develop (https://okd-dev.hbp.eu) clusters. + +This is done ahead of any deploys, is valid for all future deploys and rarely needs to be updated. + +> :warning: The process of editing template is fragile and error prone. One should be vigilant and update the template as little as possible. Ideally, add new templates and alter the deployment pipeline, rather than edit existing templates. + +The template is produced mainly by referencing Openshift container platform [template API](https://docs.openshift.com/container-platform/3.11/rest_api/template_openshift_io/template-template-openshift-io-v1.html). + +A number of sensitive variables are stored on the openshift clusters, and added to the container at runtime. They include: + +| variable name | from | description | +| --- | --- | --- | +| `REDIS_PASSWORD` | `okd_secret.redis-rate-limiting-db-ephemeral.database-password` | password to redis | +| `*` | `okd_configmap.hbp-oauth-config-map.*` | Contains Client ID, Client secret etc for oauth with EBRAINS IAM service | +| `*` | `okd_configmap.fluent-logging.*` | Contains fluentd logging variables | +| `*` | `okd_configmap.plugins.*` | Contains plugins variables | +| `*` | `okd_configmap.other-deploy-config.*` | Contains other deploy variables | + +### [3] Deployment parameters + +Per [deployment template](./okd-branch-tmpl.yaml), a number of parameters may be required when creating new deployments. + +| name | required | desc | +| --- | --- | --- | +| `SESSION_SECRET` | | Random strings to encrypt sessions. Not currently used. | +| `BRANCH_NAME` | true | Determines the tag of the image to pull. | +| `ROUTE_HOST` | true | host field of route service of the deployed template. Possible values are `*.apps.hbp.eu` for prod, `*.apps-dev.hbp.eu` for dev. Must **not** end with `/` | +| `ROUTE_PATH` | | path field of route service of the deployed template. If set, **must** start with `/` | +| `DEPLOY_ID` | true | Strip all special characters from `BRANCH_NAME`. Distinguishes one deployment from another. | +| `BUILD_TEXT` | | Shows as over lay text, to mark dev build. Defaults to `dev build` | \ No newline at end of file diff --git a/.openshift/v2/okd_branch_tmpl_v2.yaml b/.openshift/v2/okd_branch_tmpl_v2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c5af1ef048c81a452243db70e731a1d90bffb026 --- /dev/null +++ b/.openshift/v2/okd_branch_tmpl_v2.yaml @@ -0,0 +1,144 @@ +apiVersion: v1 +kind: Template +metadata: + name: siibra-explorer-branch-deploy-2 + annotations: + description: "Deploy branch of siibra-explorer" + tags: "nodejs,siibra-explorer" +objects: +- apiVersion: v1 + kind: DeploymentConfig + metadata: + name: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + labels: + app: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + spec: + replicas: 3 + revisionHistoryLimit: 10 + selector: + deploymentconfig: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + template: + metadata: + labels: + app: siibra-explorer-branch-deploy + deploymentconfig: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + spec: + containers: + - env: + - name: SESSION_SECRET + value: ${SESSION_SECRET} + - name: HOSTNAME + value: ${ROUTE_HOST} + - name: HOST_PATHNAME + value: ${ROUTE_PATH} + + - name: BUILD_TEXT + value: ${BUILD_TEXT} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + key: database-password + name: redis-rate-limiting-db-ephemeral + envFrom: + - configMapRef: + name: hbp-oauth-config-map + - configMapRef: + name: fluent-logging + - configMapRef: + name: plugins + - configMapRef: + name: other-deploy-config + + image: "docker-registry.ebrains.eu/siibra/siibra-explorer:${BRANCH_NAME}" + imagePullPolicy: Always + livenessProbe: + failureThreshold: 3 + httpGet: + path: ${ROUTE_PATH}/ready + port: 8080 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + httpGet: + path: ${ROUTE_PATH}/ready + port: 8080 + scheme: HTTP + initialDelaySeconds: 3 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 6 + name: siibra-explorer-2-${DEPLOY_ID} + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 +- apiVersion: v1 + kind: Service + metadata: + labels: + app: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + name: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + spec: + ports: + - name: 8080-tcp + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + deploymentconfig: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + type: ClusterIP +- apiVersion: v1 + kind: Route + metadata: + labels: + app: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + name: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + spec: + host: ${ROUTE_HOST} + path: ${ROUTE_PATH} + port: + targetPort: 8080-tcp + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: siibra-explorer-branch-deploy-2-${DEPLOY_ID} + weight: 100 + wildcardPolicy: None + +parameters: +- description: Session secret + from: '[A-Z0-9]{16}' + generate: expression + name: SESSION_SECRET + +- name: BRANCH_NAME + required: true +- name: DEPLOY_ID + required: true + description: | + ID that distinguish deployments. + Use only [a-z0-9]{4,} +- name: ROUTE_HOST + description: route/host for the deployed service. Must be unique, or route may not be deployed. Must **NOT** end with with /. + required: true +- name: ROUTE_PATH + description: path for the deployed service. May be left empty. If set, must start with /. + value: '' +- name: BUILD_TEXT + description: 'UI displaying which build' + value: 'dev build' +labels: + template: siibra-explorer-branch-deploy-template-v2 diff --git a/deploy/server.js b/deploy/server.js index fdc0fbbbc27c19aafcdb0f989140bf5281000ba2..f628ffe7582de3f3d80178137634fd72885e2e40 100644 --- a/deploy/server.js +++ b/deploy/server.js @@ -7,9 +7,10 @@ if (process.env.NODE_ENV !== 'production') { if (process.env.FLUENT_HOST) { const Logger = require('./logging') + const os = require('os') const name = process.env.IAV_NAME || 'IAV' - const stage = process.env.IAV_STAGE || 'unnamed-stage' + const stage = os.hostname() || 'unknown-host' const protocol = process.env.FLUENT_PROTOCOL || 'http' const host = process.env.FLUENT_HOST || 'localhost' diff --git a/deploy_env.md b/deploy_env.md index ca6a5e72c6aa542a6b88471f88eb3174a3b2e05f..bb17c783cbd86d4fa1932a36f44f7a4118012452 100644 --- a/deploy_env.md +++ b/deploy_env.md @@ -47,7 +47,6 @@ | `FLUENT_HOST` | host for fluent logging | `localhost` | | `FLUENT_PORT` | port for fluent logging | 24224 | | `IAV_NAME` | application name to be logged | `IAV` | -| `IAV_STAGE` | deploy of the application | `unnamed-stage` | ##### CSP diff --git a/docs/releases/v2.5.4.md b/docs/releases/v2.5.4.md index dcd355d798c930d12a3977f8ac4368d4a8078cce..0f8e0417b4597e4e55115d5d2f88a3331501fbe2 100644 --- a/docs/releases/v2.5.4.md +++ b/docs/releases/v2.5.4.md @@ -3,3 +3,4 @@ ## Under the hood - Added version check for siibra-api +- Updated deploy template diff --git a/e2e/checklist.md b/e2e/checklist.md new file mode 100644 index 0000000000000000000000000000000000000000..7577819cb3cf150a1fc8588a165c9224b57290c0 --- /dev/null +++ b/e2e/checklist.md @@ -0,0 +1,51 @@ +# Staging Checklist + +**use incognito browser** + +[home page](https://siibra-explorer.apps.hbp.eu/staging/) + +## General + +- [ ] Can access front page +- [ ] Can login to oidc v2 via top-right + +## Atlas data specific + +- [ ] Human multilevel atlas + - [ ] on click from home page, MNI152, Julich v2.9 loads without issue + - [ ] on hover, show correct region name(s) + - [ ] regional is fine :: select hOC1 right + - [ ] probabilistic map loads fine + - [ ] segmentation layer hides + - [ ] `navigate to` button exists, and works + - [ ] `Open in KG` button exists and works + - [ ] `Description` tabs exists and works + - [ ] `Regional features` tab exists and works + - [ ] `Receptor density` dataset exists and works + - [ ] `Open in KG` button exists and works + - [ ] `Preview` tab exists and works + - [ ] fingerprint is shown, interactable + - [ ] profiles can be loaded, interactable + - [ ] AR can be loaded + - [ ] `IEEG recordings` exists and works (at least 3) + - [ ] GDPR warning triangle + - [ ] `Open in KG` button exists and works + - [ ] perspective view works + - [ ] mesh becomes transparent + - [ ] mesh transparency returns when exit the panel + - [ ] electrodes appear in perspective view + - [ ] some contact points should apepar red (intersect with region) + - [ ] electrode tab + - [ ] show should a number of contact points + - [ ] clicking on electrode should navigate to the contact point location + - [ ] `Connectivity` tab exists and works + - [ ] on opening tab, PMap disappear, colour mapped segmentation appears + - [ ] on closing tab, PMap reappear, segmentation hides + - [ ] Explore in other templates exists, and has MNI152 and big brain + - [ ] clicking on the respective space will load julich 2.9 in that space + - [ ] the navigation should be preserved + - [ ] in big brain v2.9 (or latest) + - [ ] high res hoc1, hoc2, hoc3, lam1-6 are visible +- [ ] Waxholm + - [ ] v4 are visible + - [ ] on hover, show correct region name(s) \ No newline at end of file