diff --git a/README.md b/README.md index 25325ade..044ab37d 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,102 @@ -## How to build and develop using the new dockerrized app. +## How to build and develop using the new dockerized app. + +### Prerequisites + +If not already satisfied, add the following entry into your `/etc/hosts` file at the very top: + +``` +127.0.0.1 fakeservices.datajoint.io +``` + +This will create an alias to your `localhost` based on requests to `fakeservices.datajoint.io`. + +Make sure to also define a `.env` file as follows: + +``` sh +# minimum +DJ_PASS=db_password +AWS_ACCESS_KEY_ID=aws_key +AWS_SECRET_ACCESS_KEY=aws_secret +DEMO_PASSWORD=ibl_navigator_password +JWT_SECRET=secret +# utilized for remote deployment +SUBDOMAINS=sub +URL=example.com +# utilized for load testing +TEST_DJ_HOST=test_db_host +TEST_DJ_USER=test_db_user +TEST_DJ_PASS=test_db_password +``` + +### Versioning + +Versioning is now handled in `version.env`. Make sure to increment the version as appropriate to service based on [semantic versioning](https://semver.org/) guidelines. For services specific to `public` development, version should indicate as such e.g. `vX.X.X-public`. To determine the global version for git tagging, utilize a vector summation between python backend, node backend, frontend, and nginx. If related to `public` development, make sure to additionally add the `-public` suffix. Additionally, version can be inspected within node backend by sending a request to `/version`. To properly handle versioning both for image tags and api requests, you will need to prepend your `docker-compose` commands with `env $(grep -hv '^#' version.env) `. + +### Build To be 100% sure of the new build to be reflected - use below -`docker-compose -f docker-compose-dev.yml build --no-cache` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml build --no-cache` Then, -`docker-compose -f docker-compose-dev.yml up` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml up` to begin the development in `ng serve` mode - go to -localhost:9000 to see the site. -`docker-compose -f docker-compose-dev.yml down` -when done developing. +fakeservices.datajoint.io:9000 to see the site. +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml down` +when done developing. **Note: When utilizing `dev` and `test` docker-compose files, `ibl-frontend/frontend-content/src/environments/*.ts` files are overwritten due to volume mount. Make sure not to commit these updates.** For casual re-build/up process -`docker-compose -f docker-compose-dev.yml up --build` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml up --build` For detached mode and to add log after the fact -`docker-compose -f docker-compose-dev.yml up -d` -`docker-compose -f docker-compose-dev.yml logs -f` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml up -d` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml logs -f` -To see the production build using `ng build --prod`, -do the regular docker-compose up then go to localhost:8080 -`docker-compose up --build` +to do the regular docker-compose up then go to localhost:9000 +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-build.yml up --build` to check inside docker -`docker-compose -f docker-compose-dev.yml exec ibl-node-server /bin/bash` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml exec ibl-node-server /bin/bash` -------------------------------- for deploy (general) -Before building, make sure `build: ./ibl-frontend` is UNcommented in docker-compose.yml. -`docker-compose build ibl-navigator` once that's built, -`docker push registry.vathes.com/ibl-navigator/frontend:v0.0` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-build.yml build ibl-navigator` once that's built, +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-build.yml push ibl-navigator` -commentout the `build: ./ibl-frontend` - -repeat for other 3 `iblapi` `ibl-node-server` `nginx` and push to appropriate directory. Update the tags accordingly as well. +repeat for other 2 `iblapi` `ibl-node-server` and push to appropriate directory. Update the tags accordingly as well. for testdev deploy comment out test/* directory in `.dockerignore` (until proper storage solution is in place) -for test dev mode, make sure `STAGING=true` for nginx > environment setting. + +make sure to update `SUBDOMAINS` key in `.env` file to `testdev`. +make sure to update `URL` key in `.env` file to `datajoint.io`. + +for test dev mode, in `docker-compose-deploy.yml` make sure `STAGING=true` for `letsencrypt` > environment setting. `ssh testdev` go to `ibl-navigator` -`docker-compose down` to stop what's already running +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml down` to stop what's already running `sudo rm -R letsencrypt-keys` to get rid of key folder that generated in the previous run. -`git pull origin dev` to get the latest from `mahos/ibl-navigator` repo. +`git pull origin dev` to get the latest from `vathes/ibl-navigator` repo. make sure to move over to the `dev` branch by `git checkout dev` `docker login registry.vathes.com` to docker to get access. -`docker-compose pull` to get the ibl-navigator container -`docker-compose up --build -d` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml pull` to get the ibl-navigator container +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml up -d` ----------------------------------- for real deploy -for client deploy mode, comment out `STAGING=true` for nginx > environment setting. + +make sure to update `SUBDOMAINS` key in `.env` file to `djcompute`. +make sure to update `URL` key in `.env` file to `internationalbrainlab.org`. + +for client deploy mode, in `docker-compose-deploy.yml` make sure to comment out `STAGING=true` for `letsencrypt` > environment setting. `ssh djcompute` go to `nagivator-deployer/ibl-navigator` -`docker-compose down` to stop what's already running -`git pull origin master` to get the latest from `mahos/ibl-navigator` repo. +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml down` to stop what's already running +`git pull origin master` to get the latest from `vathes/ibl-navigator` repo. make sure to move over to the `master` branch by `git checkout master` `docker login registry.vathes.com` to docker to get access. -`docker-compose pull` to get the ibl-navigator container -`docker-compose up --build -d` +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml pull` to get the ibl-navigator container +`env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml up -d` ------------------------------------- \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 1265cc85..55aeff05 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,22 +1,29 @@ -FROM datajoint/jupyter:python3.6 +FROM raphaelguzman/djlab:py3.6-debian -# for production builds -ADD . /src/iblapi - -#RUN pip uninstall -y datajoint && pip install git+https://github.com/dimitri-yatsenko/datajoint-python.git@dev#egg=datajoint-python +# RUN \ +# pip uninstall -y datajoint && \ +# pip install \ +# git+https://github.com/dimitri-yatsenko/datajoint-python.git@dev#egg=datajoint-python RUN pip install --upgrade --pre datajoint -RUN \ - pip install -e /src/iblapi && \ - chmod +x /src/iblapi/run-ibl-api.prod.sh && \ - chmod +x /src/iblapi/run-ibl-api.dev.sh - HEALTHCHECK \ --timeout=3s \ --retries=20 \ CMD \ - curl --fail http://localhost:5000/v0/lab || exit 1 + wget --quiet --tries=1 --spider http://localhost:5000/v0/lab > /dev/null 2>&1 || exit 1 ENTRYPOINT ["/src/iblapi/run-ibl-api.prod.sh"] + +# for production builds +RUN mkdir -p /src/iblapi +COPY --chown=dja:anaconda ["notebooks", "/src/iblapi/notebooks"] +COPY --chown=dja:anaconda ["./*.txt", "./*.sh", "./*.rst", "./*.py", "/src/iblapi/"] + +RUN \ + pip install -e /src/iblapi && \ + chmod +x /src/iblapi/run-ibl-api.prod.sh && \ + chmod +x /src/iblapi/run-ibl-api.dev.sh + +# COPY --chown=dja:anaconda ["tests", "/src/iblapi/tests"] \ No newline at end of file diff --git a/backend/iblapi.py b/backend/iblapi.py index 51f19fd6..86bf7d16 100644 --- a/backend/iblapi.py +++ b/backend/iblapi.py @@ -45,6 +45,8 @@ def test_mkvmod(mod): ephys = mkvmod('ephys') histology = mkvmod('histology') test_histology = test_mkvmod('histology') +original_max_join_size = dj.conn().query( + "show variables like 'max_join_size'").fetchall()[0][1] dj.config['stores'] = { 'ephys': dict( @@ -103,6 +105,7 @@ def dumps(cls, obj): reqmap = { '_q': None, + '_health': None, 'lab': reference.Lab, 'labmember': reference.LabMember, 'labmembership': reference.LabMembership, @@ -186,6 +189,8 @@ def do_req(subpath): abort(404) elif obj == '_q': return handle_q(pathparts[1], args, proj, **kwargs) + elif obj == '_health': + return dumps(dict(healthy=True)) else: q = (reqmap[obj] & args) if proj: @@ -215,7 +220,8 @@ def handle_q(subpath, args, proj, **kwargs): post_process = None if subpath == 'sessionpage': sess_proj = acquisition.Session().aggr( - acquisition.SessionProject().proj('session_project', dummy2='"x"') * dj.U('dummy2'), + acquisition.SessionProject().proj('session_project', dummy2='"x"') + * dj.U('dummy2'), session_project='IFNULL(session_project, "unassigned")', keep_all_rows=True ) @@ -229,12 +235,19 @@ def handle_q(subpath, args, proj, **kwargs): ephys.ProbeInsertion().proj(dummy2='"x"') * dj.U('dummy2'), nprobe='count(dummy2)', keep_all_rows=True) - # training_status = acquisition.Session.aggr(analyses_behavior.SessionTrainingStatus.proj(dummy3='"x"') * dj.U('dummy3'), nstatus='count(dummy3)', keep_all_rows=True) - q = (acquisition.Session() * sess_proj * psych_curve * ephys_data * subject.Subject() * subject.SubjectLab() * subject.SubjectUser() * analyses_behavior.SessionTrainingStatus() - & ((reference.Lab() * reference.LabMember()) + training_status = acquisition.Session.aggr( + analyses_behavior.SessionTrainingStatus.proj(dummy3='"x"') * dj.U('dummy3'), + nstatus='count(dummy3)', + keep_all_rows=True) + + q = (acquisition.Session() * sess_proj * psych_curve * ephys_data * training_status + * subject.Subject() * subject.SubjectLab() + & (reference.Lab() * reference.LabMember() & reference.LabMembership().proj('lab_name', 'user_name')) - & args) - # q = acquisition.Session() * sess_proj * psych_curve * ephys_data * training_status * subject.Subject() * subject.SubjectLab() & ((reference.Lab() * reference.LabMember() & reference.LabMembership().proj('lab_name', 'user_name'))) + & args) + dj.conn().query("SET SESSION max_join_size={}".format('18446744073709551615')) + q = q.proj(*proj).fetch(**kwargs) if proj else q.fetch(**kwargs) + dj.conn().query("SET SESSION max_join_size={}".format(original_max_join_size)) elif subpath == 'subjpage': print('Args are:', args) proj_restr = None @@ -412,10 +425,8 @@ def post_process(ret): else: abort(404) - if proj: - ret = q.proj(*proj).fetch(**kwargs) - else: - ret = q.fetch(**kwargs) + ret = q if isinstance(q, (list, dict)) else (q.proj(*proj).fetch(**kwargs) + if proj else q.fetch(**kwargs)) # print('D type', ret.dtype) # print(ret) diff --git a/docker-compose-base.yml b/docker-compose-base.yml new file mode 100644 index 00000000..98b167ba --- /dev/null +++ b/docker-compose-base.yml @@ -0,0 +1,97 @@ +# Do NOT `docker-compose up` this file. +# This is simply a base that other docker-compose files extend based on needs. +version: '2.4' +x-net: &net + networks: + - main +services: + iblapi: + <<: *net + image: registry.vathes.com/ibl-navigator/iblapi:${DJ_API_VERSION} + environment: + - DJ_USER=maho + - DJ_HOST=datajoint.internationalbrainlab.org + # for public demo + # - DJ_USER=ibl-navigator + # - DJ_HOST=datajoint-public.internationalbrainlab.org + - AWS_DEFAULT_REGION=us-east-1 + - WORKER_COUNT=4 + env_file: ./.env + healthcheck: + test: > + wget --quiet --tries=1 --spider \ + http://localhost:5000/v0/_health > /dev/null 2>&1 || exit 1 + timeout: 3s + retries: 20 + entrypoint: ["/src/iblapi/run-ibl-api.prod.sh"] + # cpus: 0.65 + #cpu_quota: 100000 + #cpu_period: 100ms + ibl-navigator: + <<: *net + image: registry.vathes.com/ibl-navigator/frontend:${FRONTEND_VERSION} + healthcheck: + test: curl --fail http://localhost:9000 || exit 1 + timeout: 3s + retries: 20 + ibl-node-server: + <<: *net + image: registry.vathes.com/ibl-navigator/node-server:${NODE_API_VERSION} + environment: + - NODE_ENV=development + - DEMO_USERNAME=ibluser + - PY_BACKEND=http://iblapi:5000 + env_file: + - .env + - version.env + healthcheck: + test: curl --fail http://localhost:3333/version || exit 1 + timeout: 3s + retries: 20 + # interval: 1s + letsencrypt: + <<: *net + image: linuxserver/letsencrypt:amd64-0.38.0-ls62 + environment: + - PUID=1000 + - PGID=1000 + - EMAIL=service-health@vathes.com + - TZ=Europe/London + - ONLY_SUBDOMAINS=true + - VALIDATION=http + healthcheck: + test: /healthcheck.sh + timeout: 5s + retries: 300 + interval: 1s + # depends_on: + # nginx: + # condition: service_healthy + nginx: + <<: *net + image: raphaelguzman/nginx:${DJ_NGINX_VERSION} + environment: + - ADD_frontend_TYPE=REST + - ADD_frontend_ENDPOINT=ibl-navigator:9000 + - ADD_frontend_PREFIX=/ + - ADD_nodebackend_TYPE=REST + - ADD_nodebackend_ENDPOINT=ibl-node-server:3333 + - ADD_nodebackend_PREFIX=/api + - HTTPS_PASSTHRU=TRUE + - CERTBOT_HOST=letsencrypt:80 + ports: + - "80:80" + - "443:443" + # depends_on: + # iblapi: + # condition: service_healthy + # ibl-node-server: + # condition: service_healthy + # ibl-navigator: + # condition: service_healthy +networks: + main: + ipam: + driver: default + config: + - subnet: 10.28.0.0/16 diff --git a/docker-compose-build.yml b/docker-compose-build.yml new file mode 100644 index 00000000..22872911 --- /dev/null +++ b/docker-compose-build.yml @@ -0,0 +1,43 @@ +# Use: Production build and manual test. Provided things look good, you may follow with a push. +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-build.yml up --build +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-build.yml push +version: '2.4' +x-net: &net + networks: + - main +services: + iblapi: + extends: + file: ./docker-compose-base.yml + service: iblapi + build: ./backend + <<: *net + ibl-navigator: + extends: + file: ./docker-compose-base.yml + service: ibl-navigator + build: ./ibl-frontend + environment: + - PROD_NODE_BACKEND=https://fakeservices.datajoint.io/api + <<: *net + ibl-node-server: + extends: + file: ./docker-compose-base.yml + service: ibl-node-server + build: ./node_server + <<: *net + nginx: + extends: + file: ./docker-compose-base.yml + service: nginx + environment: + - SUBDOMAINS=fakeservices + - URL=datajoint.io + - CERTBOT_HOST= + <<: *net +networks: + main: + ipam: + driver: default + config: + - subnet: 10.28.0.0/16 diff --git a/docker-compose-deploy.yml b/docker-compose-deploy.yml new file mode 100644 index 00000000..08ea2b15 --- /dev/null +++ b/docker-compose-deploy.yml @@ -0,0 +1,53 @@ +# Use: Production remote deployment. +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml pull +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-deploy.yml up +version: '2.4' +x-net: &net + networks: + - main +services: + iblapi: + extends: + file: ./docker-compose-base.yml + service: iblapi + <<: *net + ibl-navigator: + extends: + file: ./docker-compose-base.yml + service: ibl-navigator + environment: + - PROD_NODE_BACKEND=https://${SUBDOMAINS}.${URL}/api + <<: *net + ibl-node-server: + extends: + file: ./docker-compose-base.yml + service: ibl-node-server + <<: *net + letsencrypt: + extends: + file: ./docker-compose-base.yml + service: letsencrypt + environment: + - SUBDOMAINS + - URL + - STAGING=true # Uncomment for Production use + volumes: + - ./letsencrypt-keys:/config/etc/letsencrypt + - ./letsencrypt/healthcheck.sh:/healthcheck.sh + <<: *net + nginx: + extends: + file: ./docker-compose-base.yml + service: nginx + environment: + - SUBDOMAINS + - URL + volumes: + - ./letsencrypt-keys:/etc/letsencrypt:ro + <<: *net +networks: + main: + ipam: + driver: default + config: + - subnet: 10.28.0.0/16 diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 955ee2d4..3e97870b 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -1,70 +1,51 @@ -# docker-compose up -d --build +# Use: Local development. +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-dev.yml up --build version: '2.4' -services: +x-net: &net + networks: + - main +services: iblapi: - build: ./backend - image: iblapi:v1.0 - environment: - - DJ_USER=maho - - DJ_HOST=datajoint.internationalbrainlab.org - # - DJ_USER=ibl-navigator # from publicdemo - # - DJ_HOST=datajoint-public.internationalbrainlab.org # from publicdemo - # - AWS_DEFAULT_REGION=us-east-1 # from publicdemo - - WORKER_COUNT=4 - env_file: ./.env + extends: + file: ./docker-compose-build.yml + service: iblapi ports: - - "5555:5000" - healthcheck: # from publicdemo - test: curl --fail http://localhost:5000/v0/lab || exit 1 - timeout: 3s - retries: 20 - entrypoint: ["/src/iblapi/run-ibl-api.prod.sh"] + - "5000:5000" # iblapi #entrypoint: ["/src/iblapi/run-ibl-api.dev.sh", "development"] - networks: - - main + <<: *net ibl-navigator: + extends: + file: ./docker-compose-build.yml + service: ibl-navigator build: context: ./ibl-frontend - dockerfile: Dockerfile.dev - image: ibl-navigator:v1.0 + dockerfile: dev.dockerfile + environment: + # - DEV_NODE_BACKEND=http://localhost:3333/api + - DEV_NODE_BACKEND=https://fakeservices.datajoint.io/api volumes: - - ./ibl-frontend/frontend-content/src:/frontend/src - - ./ibl-frontend/frontend-content/angular.json:/frontend/angular.json - healthcheck: # from publicdemo - test: curl --fail http://localhost:9000 || exit 1 - timeout: 3s - retries: 20 - ports: - - "9000:9000" - networks: - - main - depends_on: - ibl-node-server: - condition: service_healthy - iblapi: - condition: service_healthy + - ./ibl-frontend/frontend-content/src:/app/src + - ./ibl-frontend/frontend-content/angular.json:/app/angular.json + <<: *net ibl-node-server: - build: ./node_server - image: ibl-node-server:v1.0 + extends: + file: ./docker-compose-build.yml + service: ibl-node-server # command: nodemon /src/server.js volumes: - ./node_server/app.js:/src/app.js - environment: - - NODE_ENV=development - - DEMO_USERNAME=ibluser - - PY_BACKEND=http://iblapi:5000 - env_file: .env - healthcheck: # from publicdemo - test: curl --fail http://localhost:3333/version || exit 1 - timeout: 3s - retries: 20 + <<: *net + nginx: + extends: + file: ./docker-compose-build.yml + service: nginx ports: - - "3333:3333" - networks: - - main + - "3333:3333" # node-server + - "9000:9000" # frontend + <<: *net networks: - main: - ipam: - driver: default - config: - - subnet: 10.28.0.0/16 \ No newline at end of file + main: + ipam: + driver: default + config: + - subnet: 10.28.0.0/16 diff --git a/docker-compose-test.yml b/docker-compose-test.yml new file mode 100644 index 00000000..0f4af327 --- /dev/null +++ b/docker-compose-test.yml @@ -0,0 +1,88 @@ +# Use: Continuous integration testing. +# env $(grep -hv '^#' version.env) docker-compose -f docker-compose-test.yml up --build --exit-code-from load +version: '2.4' +x-net: &net + networks: + - main +services: + iblapi: + extends: + file: ./docker-compose-dev.yml + service: iblapi + environment: + - DJ_HOST=${TEST_DJ_HOST} + - DJ_USER=${TEST_DJ_USER} + - DJ_PASS=${TEST_DJ_PASS} + - WORKER_COUNT=1 + entrypoint: ["/bin/sh"] + command: > + -c + " + ## init test db + # pip install git+https://github.com/int-brain-lab/IBL-pipeline.git + # MODE=update python /src/iblapi/tests/schema.py; + ## serve prod + /src/iblapi/run-ibl-api.prod.sh & + ## perform tests + ## pass healthcheck + touch /tmp/pass_check; + tail -f /dev/null; + " + healthcheck: + test: wget --quiet --tries=1 --spider http://localhost:5000/v0/_health > /dev/null 2>&1 && [ -f "/tmp/pass_check" ] || exit 1 + timeout: 3s + retries: 20 + <<: *net + ibl-navigator: + extends: + file: ./docker-compose-dev.yml + service: ibl-navigator + <<: *net + ibl-node-server: + extends: + file: ./docker-compose-dev.yml + service: ibl-node-server + <<: *net + fakeservices.datajoint.io: + extends: + file: ./docker-compose-dev.yml + service: nginx + <<: *net + load: + image: loadimpact/k6:0.26.2 + environment: + - SUBDOMAIN=fakeservices + - DOMAIN=datajoint.io + - DEMO_USERNAME=ibluser + - DEMO_PASSWORD=${DEMO_PASSWORD} + ## w/ WORKER_COUNT=4 + - VUS=19 # t2 100.0% + # - VUS=14 # t3 73.7% + # - VUS=9 # t3a 47.4% + ## w/ WORKER_COUNT=1 + # - VUS=13 # t2 100.0% + # - VUS=13 # t3 100.0% + # - VUS=7 # t3a 53.8% + - FAILURE_RATE="0.15" + user: root + working_dir: /tmp + entrypoint: sh + command: -c "./k6_run.sh" + volumes: + - ./load:/tmp + depends_on: + iblapi: + condition: service_healthy + ibl-navigator: + condition: service_healthy + ibl-node-server: + condition: service_healthy + fakeservices.datajoint.io: + condition: service_healthy + <<: *net +networks: + main: + ipam: + driver: default + config: + - subnet: 10.28.0.0/16 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index e576ff78..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,154 +0,0 @@ -# docker-compose up -d --build -version: '2.4' -services: - iblapi: - # build: ./backend - image: registry.vathes.com/ibl-navigator/iblapi:v0.14 # for client deploy - # image: registry.vathes.com/ibl-navigator/iblapi:v0.2-dev16 # add -dev ending to testdev tag - # image: registry.vathes.com/ibl-navigator/iblapi:v0.0-public3 # for public demo - environment: - - DJ_USER=maho - # - DJ_USER=ibl-navigator # for public demo - - DJ_HOST=datajoint.internationalbrainlab.org - # - DJ_HOST=datajoint-public.internationalbrainlab.org # for public demo - - AWS_DEFAULT_REGION=us-east-1 - - WORKER_COUNT=4 - env_file: ./.env - healthcheck: - test: curl --fail http://localhost:5000/v0/lab || exit 1 - timeout: 3s - retries: 20 - # volumes: - # - ./backend:/src/iblapi - # ports: - # - "5000:5000" - entrypoint: ["/src/iblapi/run-ibl-api.prod.sh"] - # entrypoint: ["/src/iblapi/run-ibl-api.dev.sh", "development"] - networks: - - main - # cpus: 0.65 - #cpu_quota: 100000 - #cpu_period: 100ms - ibl-navigator: - # build: ./ibl-frontend - image: registry.vathes.com/ibl-navigator/frontend:v0.17 # for client deploy - # image: registry.vathes.com/ibl-navigator/frontend:v0.2-dev21 # add -dev ending to testdev tag - # image: registry.vathes.com/ibl-navigator/frontend:v0.0-public8 # for public demo - environment: - # - PROD_NODE_BACKEND=https://testdev.datajoint.io/api # for testdev deploy - - PROD_NODE_BACKEND=https://djcompute.internationalbrainlab.org/api # for client deploy - # - PROD_NODE_BACKEND=https://data.internationalbrainlab.org/api # for public demo deploy - # - PROD_NODE_BACKEND=http://localhost:3333 - # - DEV_NODE_API=http://localhost:9000/api - # - DEV_NODE_BACKEND=http://localhost:9000 - healthcheck: - test: curl --fail http://localhost:8080 || exit 1 - timeout: 3s - retries: 20 - # ports: - # - "8080:8080" - networks: - - main - ibl-node-server: - # build: ./node_server - image: registry.vathes.com/ibl-navigator/node-server:v0.9 # for client deploy - # image: registry.vathes.com/ibl-navigator/node-server:v0.2-dev7 # add -dev ending to testdev tag - # image: registry.vathes.com/ibl-navigator/node-server:v0.0-public3 # for public demo - environment: - - NODE_ENV=development - - DEMO_USERNAME=ibluser - - PY_BACKEND=http://iblapi:5000 - env_file: .env - healthcheck: - test: curl --fail http://localhost:3333/version || exit 1 - timeout: 3s - retries: 20 - # interval: 1s - # volumes: - # - ./node_server:/src - # ports: - # - "3333:3333" - networks: - - main - letsencrypt: - image: linuxserver/letsencrypt:amd64-0.38.0-ls62 - # image: linuxserver/letsencrypt:amd64-0.38.0-ls62 # from publicdemo - environment: - - PUID=1000 - - PGID=1000 - - EMAIL=service-health@vathes.com - # - URL=datajoint.io # for testdev deploy - - URL=internationalbrainlab.org # for client or public demo deploy - # - SUBDOMAINS=testdev # for testdev deploy - - SUBDOMAINS=djcompute # for client deploy - # - SUBDOMAINS=data # for public demo deploy - - TZ=Europe/London - - ONLY_SUBDOMAINS=true - - STAGING=true # UNcomment for testdev - - VALIDATION=http - healthcheck: - # test: ["CMD-SHELL","if","[", "$$(expr", "$$(date", "+%s)","-","$$(stat","-c","%Y","$$(ls","-la","/config/etc/letsencrypt/live/*.*/fullchain.pem","|","awk","'{print","sprintf(\"%s/%s\",\"/config/etc/letsencrypt/live/*.*\",$$11)}')))","-lt","\"7776000\"","];","then","exit","0;else","exit","1;fi"] - # test: if [ $$(expr $(date +%s) - $$(stat -c %Y $(ls -la /config/etc/letsencrypt/live/*.*/fullchain.pem | awk '{print sprintf("%s/%s", "/config/etc/letsencrypt/live/*.*", $11)}'))) -lt "7776000" ]; then exit 0;else exit 1;fi - test: /healthcheck.sh - timeout: 5s - retries: 300 - interval: 1s - # ports: - # - "80:80" - # - "443:443" - # cap_add: - # - NET_ADMIN - volumes: - - ./letsencrypt-keys:/config/etc/letsencrypt - - ./letsencrypt/healthcheck.sh:/healthcheck.sh - networks: - - main - # depends_on: - # ibl-node-server: - # condition: service_healthy - # iblapi: - # condition: service_healthy - # ibl-navigator: - # condition: service_healthy - nginx: - # build: ./nginx - image: registry.vathes.com/ibl-navigator/nginx:v0.1 # for client deploy - # image: registry.vathes.com/ibl-navigator/nginx:v0.1-dev4 - # image: registry.vathes.com/ibl-navigator/nginx:v0.0-public2 # for public deploy ? - environment: - # - URL=datajoint.io # for testdev deploy - - URL=internationalbrainlab.org # for client or public demo deploy - # - SUBDOMAINS=testdev # for testdev deploy - - SUBDOMAINS=djcompute # for client deploy - # - SUBDOMAINS=data # for public demo deploy - - NODE_SERVER=http://ibl-node-server:3333 - - FRONTEND_SERVER=http://ibl-navigator:8080 - - LETSENCRYPT_SERVER=http://letsencrypt:80 - healthcheck: - test: wget --no-check-certificate --quiet --tries=1 --spider https://localhost:443 || exit 1 - # wget --quiet --tries=1 --spider https://testdev.datajoint.io:443 || exit 1 - timeout: 5s - retries: 300 - interval: 1s - ports: - - "80:80" - - "443:443" - # cap_add: - # - NET_ADMIN - volumes: - - ./letsencrypt-keys:/etc/letsencrypt:ro - networks: - - main - # depends_on: - # ibl-node-server: - # condition: service_healthy - # iblapi: - # condition: service_healthy - # ibl-navigator: - # condition: service_healthy -networks: - main: - ipam: - driver: default - config: - - subnet: 10.28.0.0/16 diff --git a/ibl-frontend/Dockerfile b/ibl-frontend/Dockerfile index 496f719c..80462af8 100644 --- a/ibl-frontend/Dockerfile +++ b/ibl-frontend/Dockerfile @@ -1,103 +1,104 @@ -From nginx:1.17.2 - -RUN groupadd --gid 1000 node \ - && useradd --uid 1000 --gid node --shell /bin/bash --create-home node - -ENV NODE_VERSION 10.16.0 - -RUN buildDeps='xz-utils' \ - && ARCH= && dpkgArch="$(dpkg --print-architecture)" \ - && case "${dpkgArch##*-}" in \ - amd64) ARCH='x64';; \ - ppc64el) ARCH='ppc64le';; \ - s390x) ARCH='s390x';; \ - arm64) ARCH='arm64';; \ - armhf) ARCH='armv7l';; \ - i386) ARCH='x86';; \ - *) echo "unsupported architecture"; exit 1 ;; \ - esac \ - && set -ex \ - && apt-get update && apt-get install -y ca-certificates curl wget gnupg dirmngr $buildDeps --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* \ - && for key in \ - 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ - FD3A5288F042B6850C66B31F09FE44734EB7990E \ - 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ - DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ - C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ - B9AE9905FFD7803F25714661B63B535A4C206CA9 \ - 77984A986EBC2AA786BC0F66B01FBB92821C587A \ - 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ - 4ED778F539E3634C779C87C6D7062848A1AB005C \ - A48C2BEE680E841632CD4E44F07496B3EB3C1762 \ - B9E2F5981AA6E0CD28160D9FF13993A75599653C \ - ; do \ - gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \ - gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \ - done \ - && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \ - && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ - && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ - && grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ - && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ - && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ - && apt-get purge -y --auto-remove $buildDeps \ - && ln -s /usr/local/bin/node /usr/local/bin/nodejs - -ENV YARN_VERSION 1.16.0 - -RUN set -ex \ - && for key in \ - 6A010C5166006599AA17F08146C2130DFD2497F5 \ - ; do \ - gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \ - gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \ - gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \ - done \ - && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ - && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \ - && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ - && mkdir -p /opt \ - && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \ - && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \ - && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \ - && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz - -# COPY docker-entrypoint.sh /usr/local/bin/ -# ENTRYPOINT ["docker-entrypoint.sh"] - -# CMD [ "node" ] - -####### -RUN apt-get update - -COPY ./frontend-content /frontend +FROM vathes/angulardev:angcli7.1.4-angbuild0.11.4 -RUN \ - cd /frontend && \ - npm install -g @angular/cli > /dev/null && \ - npm install --save-dev @angular-devkit/build-angular > /dev/null +WORKDIR /app/dist/pipeline-viewer + +COPY ./entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] -COPY ./frontend-content/src/assets/addons/indigo-pink-ibl.css /frontend/node_modules/\@angular/material/prebuilt-themes/ -COPY ./frontend-content/src/assets/addons/plotly.js /frontend/node_modules/plotly.js-dist/ +COPY ./app.conf /etc/nginx/conf.d/default.conf +CMD ["nginx", "-g", "daemon off;"] +ADD ./frontend-content/package.json /app/ +ADD ./frontend-content/package-lock.json /app/ RUN \ - cd /frontend && \ - ng build --prod + cd /app && \ + npm install --save-dev @angular-devkit/build-angular > /dev/null -RUN npm install http-server -g -WORKDIR /frontend/dist/pipeline-viewer -# WORKDIR /frontend +ADD ./frontend-content /app -COPY ./entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] +COPY ./frontend-content/src/assets/addons/indigo-pink-ibl.css /app/node_modules/\@angular/material/prebuilt-themes/ +COPY ./frontend-content/src/assets/addons/plotly.js /app/node_modules/plotly.js-dist/ + +RUN \ + cd /app && \ + node --max_old_space_size=8192 /usr/local/lib/node_modules/@angular/cli/bin/ng build --prod -COPY ./app.conf /etc/nginx/conf.d/default.conf # CMD ["http-server","-p", "8080" ,"-a","0.0.0.0"] -CMD ["nginx", "-g", "daemon off;"] # CMD ["ng","serve","--host","0.0.0.0","--port","8080"] -# CMD tail -f /dev/null \ No newline at end of file +# CMD tail -f /dev/null + + + + + + + + + + + + + + + + +# RUN \ +# mkdir -p /app/node_modules && \ +# apt-get update && \ +# npm install -g @angular/cli > /dev/null && \ +# # npm install -g typescript@3.5.3 && \ +# # npm install -g @angular-devkit/schematics > /dev/null && \ +# # npm install -g --only=dev @angular-devkit/build-angular > /dev/null && \ +# npm install -g http-server && \ +# npm install -g typescript && \ +# # && \ +# # ng update +# # && \ +# cd /app && \ +# npm install --save-dev @angular-devkit/build-angular > /dev/null +# # && \ +# # ng update +# # && \ +# # npm update + +# CMD ["nginx", "-g", "daemon off;"] +# # VOLUME /app/node_modules +# # -------- + +# COPY ./app.conf /etc/nginx/conf.d/default.conf + +# COPY ./entrypoint.sh /entrypoint.sh +# RUN chmod +x /entrypoint.sh +# ENTRYPOINT ["/entrypoint.sh"] + + + +# COPY ./frontend-content /app + +# # RUN \ +# # cd /app && \ +# # npm install -g @angular/cli > /dev/null && \ +# # npm install --save-dev @angular-devkit/build-angular > /dev/null + +# COPY ./frontend-content/src/assets/addons/indigo-pink-ibl.css /app/node_modules/\@angular/material/prebuilt-themes/ +# COPY ./frontend-content/src/assets/addons/plotly.js /app/node_modules/plotly.js-dist/ + +# WORKDIR /app/dist/pipeline-viewer + +# RUN \ +# cd /app && \ +# ng build --prod + +# # RUN npm install http-server -g + + +# # WORKDIR /app + + + +# # CMD ["http-server","-p", "9000" ,"-a","0.0.0.0"] +# # CMD ["ng","serve","--host","0.0.0.0","--port","9000"] +# # CMD tail -f /dev/null \ No newline at end of file diff --git a/ibl-frontend/Dockerfile.dev b/ibl-frontend/Dockerfile.dev deleted file mode 100644 index 6ee65e6a..00000000 --- a/ibl-frontend/Dockerfile.dev +++ /dev/null @@ -1,24 +0,0 @@ -# don't build -FROM node:10.16-slim - -COPY ./frontend-content /frontend - -RUN apt-get update - -RUN \ - cd /frontend && \ - npm install && \ - npm install --only=dev &&\ - npm install -g @angular/cli > /dev/null - -COPY ./frontend-content/src/assets/addons/indigo-pink-ibl.css /frontend/node_modules/\@angular/material/prebuilt-themes/ -COPY ./frontend-content/src/assets/addons/plotly.js /frontend/node_modules/plotly.js-dist/ - -HEALTHCHECK \ - --timeout=3s \ - --retries=20 \ - CMD \ - curl --fail http://localhost:9000 || exit 1 -WORKDIR /frontend - -CMD ["ng","serve","--host","0.0.0.0","--port","9000"] diff --git a/ibl-frontend/app.conf b/ibl-frontend/app.conf index ee83a969..f0b742ea 100644 --- a/ibl-frontend/app.conf +++ b/ibl-frontend/app.conf @@ -1,12 +1,12 @@ server { - listen 8080; + listen 9000; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { - root /frontend/dist/pipeline-viewer; + root /app/dist/pipeline-viewer; index index.html index.htm; try_files $uri $uri/ /index.html; } @@ -17,7 +17,7 @@ server { # error_page 500 502 503 504 /50x.html; location = /50x.html { - root /frontend/dist/pipeline-viewer; + root /app/dist/pipeline-viewer; } } \ No newline at end of file diff --git a/ibl-frontend/dev.dockerfile b/ibl-frontend/dev.dockerfile new file mode 100644 index 00000000..c3ff43e7 --- /dev/null +++ b/ibl-frontend/dev.dockerfile @@ -0,0 +1,28 @@ +# don't build +FROM vathes/angulardev:angcli7.1.4-angbuild0.11.4 + +HEALTHCHECK \ + --timeout=3s \ + --retries=20 \ + CMD \ + curl --fail http://localhost:9000 || exit 1 + +COPY ./entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] +CMD ["ng","serve","--host","0.0.0.0","--port","9000","--disable-host-check"] + +WORKDIR /app + +ADD ./frontend-content/package.json /app/ +ADD ./frontend-content/package-lock.json /app/ +RUN \ + npm install && \ + npm install --only=dev +ADD ./frontend-content /app + +COPY ./frontend-content/src/assets/addons/indigo-pink-ibl.css /app/node_modules/\@angular/material/prebuilt-themes/ +COPY ./frontend-content/src/assets/addons/plotly.js /app/node_modules/plotly.js-dist/ + + + diff --git a/ibl-frontend/entrypoint.sh b/ibl-frontend/entrypoint.sh index 23c1879f..5cf24f47 100644 --- a/ibl-frontend/entrypoint.sh +++ b/ibl-frontend/entrypoint.sh @@ -1,9 +1,12 @@ #! /bin/bash -# sed -i "s|\$PROD_NODE_API|${PROD_NODE_API}|g" $(ls /frontend/dist/pipeline-viewer/main.*) -sed -i "s|\$PROD_NODE_BACKEND|${PROD_NODE_BACKEND}|g" $(ls /frontend/dist/pipeline-viewer/main.*) +# sed -i "s|\$PROD_NODE_API|${PROD_NODE_API}|g" $(ls /app/dist/pipeline-viewer/main.*) +sed -i "s|\$PROD_NODE_BACKEND|${PROD_NODE_BACKEND}|g" $(ls /app/dist/pipeline-viewer/main.*) -# sed -i "s|\$PROD_NODE_API|${PROD_NODE_API}|g" /frontend/src/environments/environment.prod.ts -sed -i "s|\$PROD_NODE_BACKEND|${PROD_NODE_BACKEND}|g" /frontend/src/environments/environment.prod.ts +# sed -i "s|\$PROD_NODE_API|${PROD_NODE_API}|g" /app/src/environments/environment.prod.ts +sed -i "s|\$PROD_NODE_BACKEND|${PROD_NODE_BACKEND}|g" /app/src/environments/environment.prod.ts + +# sed -i "s|\$DEV_NODE_API|${DEV_NODE_API}|g" /app/src/environments/environment.ts +sed -i "s|\$DEV_NODE_BACKEND|${DEV_NODE_BACKEND}|g" /app/src/environments/environment.ts "$@" \ No newline at end of file diff --git a/ibl-frontend/frontend-content/src/environments/environment.ts b/ibl-frontend/frontend-content/src/environments/environment.ts index 7743a3d2..f83b7073 100644 --- a/ibl-frontend/frontend-content/src/environments/environment.ts +++ b/ibl-frontend/frontend-content/src/environments/environment.ts @@ -4,8 +4,8 @@ export const environment = { production: false, - // api_url: 'http://localhost:3333/api', - backend_url: 'http://localhost:3333' + // api_url: '$DEV_NODE_API', + backend_url: '$DEV_NODE_BACKEND' }; /* diff --git a/load/k6_run.sh b/load/k6_run.sh new file mode 100755 index 00000000..bc3f3894 --- /dev/null +++ b/load/k6_run.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Basic load test that runs 3 consecutive runs of a configurable number of virtual users +# (VUS) that simultaneously access the POST /sessions endpoint and measure if the number +# of err'ed responses is less than or equal to threshold (FAILURE_RATE) consistently. +# +# Initially will query the endpoint to load cache (if applicable). + +apk add curl jq + +export TOKEN=$(curl -X POST https://${SUBDOMAIN}.${DOMAIN}/api/login \ + -H 'Content-Type: application/json' \ + -d '{"username":"'${DEMO_USERNAME}'","password":"'${DEMO_PASSWORD}'"}' | jq -r .token) + +date +curl "https://${SUBDOMAIN}.${DOMAIN}/api/sessions" -H "Authorization: Bearer ${TOKEN}" \ + -H 'Content-Type: application/json' --data-raw '{"__order":"session_start_time DESC"}' \ + -o /dev/null -w "time_total: %{time_total}s\ncode: %{http_code}\n" 2>/dev/null + +k6 run test_simultaneous_users.js && \ + k6 run test_simultaneous_users.js && \ + k6 run test_simultaneous_users.js diff --git a/load/test_simultaneous_users.js b/load/test_simultaneous_users.js new file mode 100644 index 00000000..770189d1 --- /dev/null +++ b/load/test_simultaneous_users.js @@ -0,0 +1,38 @@ +import http from 'k6/http'; +import { check, group, sleep } from 'k6'; +import { Rate } from 'k6/metrics'; + +var url = `https://${__ENV.SUBDOMAIN}.${__ENV.DOMAIN}/api/sessions`; +var bearer = __ENV.TOKEN; + +export let errorRate = new Rate('errors'); +export let options = { + vus: __ENV.VUS, + // duration: '90s', + iterations: __ENV.VUS, + // insecureSkipTLSVerify: true, + thresholds: { + errors: [`rate<=${__ENV.FAILURE_RATE}`], // <=20% errors + }, +}; + +export default function() { + group('sessions', () => { + var params = { + headers: { + 'Authorization': 'Bearer ' + bearer, + 'Content-Type': 'application/json', + 'User-Agent': 'DataJoint', + }, + }; + var data = { + "__order": "session_start_time DESC" + }; + const res = http.post(url, JSON.stringify(data), params); + const result = check(res, { + 'status is 200': (r) => r.status == 200, + }); + errorRate.add(!result); + }); + sleep(1); +} \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile deleted file mode 100644 index 55009c62..00000000 --- a/nginx/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# FROM linuxserver/letsencrypt -FROM nginx:alpine - -COPY ./base.conf /etc/nginx/conf.d/base.conf -COPY ./ssl.conf /ssl.conf - -COPY ./entrypoint.sh /entrypoint.sh -RUN \ - apk update && \ - apk add inotify-tools && \ - chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] - -# COPY ./fullchain.pem /etc/letsencrypt/live/testdev.datajoint.io/fullchain.pem -# COPY ./privkey.pem /etc/letsencrypt/live/testdev.datajoint.io/privkey.pem - -# HEALTHCHECK \ # from public demo -# --interval=1s \ -# --timeout=5s \ -# --retries=300 \ -# CMD \ -# wget --no-check-certificate --quiet --tries=1 --spider https://localhost:443 || exit 1 -# # wget --quiet --tries=20 --spider https://testdev.datajoint.io:443 || exit 1 diff --git a/nginx/base.conf b/nginx/base.conf deleted file mode 100644 index cac43971..00000000 --- a/nginx/base.conf +++ /dev/null @@ -1,13 +0,0 @@ -server { - listen 80; - server_name {{SUBDOMAINS}}.{{URL}}; - - location / { - return 301 https://$host$request_uri; - } - - location /.well-known/acme-challenge/ { - # root /var/www/certbot; - proxy_pass {{LETSENCRYPT_SERVER}}; - } -} diff --git a/nginx/entrypoint.sh b/nginx/entrypoint.sh deleted file mode 100644 index 57ef6282..00000000 --- a/nginx/entrypoint.sh +++ /dev/null @@ -1,47 +0,0 @@ -#! /bin/sh - -update_cert() { - nginx -s reload - echo "[$(date -u '+%Y-%m-%d %H:%M:%S')][DataJoint]: Certs updated." -} - -sed -i "s|{{SUBDOMAINS}}|${SUBDOMAINS}|g" /etc/nginx/conf.d/base.conf -sed -i "s|{{URL}}|${URL}|g" /etc/nginx/conf.d/base.conf -sed -i "s|{{NODE_SERVER}}|${NODE_SERVER}|g" /etc/nginx/conf.d/base.conf -sed -i "s|{{FRONTEND_SERVER}}|${FRONTEND_SERVER}|g" /etc/nginx/conf.d/base.conf -sed -i "s|{{LETSENCRYPT_SERVER}}|${LETSENCRYPT_SERVER}|g" /etc/nginx/conf.d/base.conf - -sed -i "s|{{SUBDOMAINS}}|${SUBDOMAINS}|g" /ssl.conf -sed -i "s|{{URL}}|${URL}|g" /ssl.conf -sed -i "s|{{NODE_SERVER}}|${NODE_SERVER}|g" /ssl.conf -sed -i "s|{{FRONTEND_SERVER}}|${FRONTEND_SERVER}|g" /ssl.conf -sed -i "s|{{LETSENCRYPT_SERVER}}|${LETSENCRYPT_SERVER}|g" /ssl.conf - -# /init -# nginx -g "daemon off;" -nginx - -echo "[$(date -u '+%Y-%m-%d %H:%M:%S')][DataJoint]: Waiting for initial certs" -while [ ! -d /etc/letsencrypt/archive/${SUBDOMAINS}.${URL} ]; do - sleep 5 -done - -echo "[$(date -u '+%Y-%m-%d %H:%M:%S')][DataJoint]: Enabling SSL feature" -mv /ssl.conf /etc/nginx/conf.d/ssl.conf -update_cert - -INIT_TIME=$(date +%s) -LAST_MOD_TIME=$(date -r $(echo /etc/letsencrypt/live/${SUBDOMAINS}.${URL}/$(ls -t /etc/letsencrypt/live/${SUBDOMAINS}.${URL}/ | head -n 1)) +%s) -DELTA=$(expr $LAST_MOD_TIME - $INIT_TIME) -while true; do - CURR_FILEPATH=$(ls -t /etc/letsencrypt/live/${SUBDOMAINS}.${URL}/ | head -n 1) - CURR_LAST_MOD_TIME=$(date -r $(echo /etc/letsencrypt/live/${SUBDOMAINS}.${URL}/${CURR_FILEPATH}) +%s) - CURR_DELTA=$(expr $CURR_LAST_MOD_TIME - $INIT_TIME) - if [ "$DELTA" -lt "$CURR_DELTA" ]; then - echo "[$(date -u '+%Y-%m-%d %H:%M:%S')][DataJoint]: Renewal: Reloading NGINX since \`$CURR_FILEPATH\` changed." - update_cert - DELTA=$CURR_DELTA - else - sleep 5 - fi -done \ No newline at end of file diff --git a/nginx/ssl.conf b/nginx/ssl.conf deleted file mode 100644 index 4460209e..00000000 --- a/nginx/ssl.conf +++ /dev/null @@ -1,47 +0,0 @@ -server { - listen 443 ssl; - server_name {{SUBDOMAINS}}.{{URL}}; - - ssl_certificate /etc/letsencrypt/live/{{SUBDOMAINS}}.{{URL}}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/{{SUBDOMAINS}}.{{URL}}/privkey.pem; - - # session settings - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; - - # protocols - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers on; - ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; - - # OCSP Stapling - ssl_stapling on; - ssl_stapling_verify on; - resolver 127.0.0.11 valid=30s; # Docker DNS Server - - # Optional additional headers - #add_header Content-Security-Policy "upgrade-insecure-requests"; - #add_header X-Frame-Options "SAMEORIGIN" always; - #add_header X-XSS-Protection "1; mode=block" always; - #add_header X-Content-Type-Options "nosniff" always; - #add_header X-UA-Compatible "IE=Edge" always; - #add_header Cache-Control "no-transform" always; - #add_header Referrer-Policy "same-origin" always; - - ### - #include /config/nginx/ssl.conf; - - location / { - proxy_pass {{FRONTEND_SERVER}}; - } - - location /login { - proxy_pass {{NODE_SERVER}}; - } - - location /api/ { - proxy_pass {{NODE_SERVER}}/; - } - -} diff --git a/node_server/Dockerfile b/node_server/Dockerfile index c078ce6a..b1ff07e8 100644 --- a/node_server/Dockerfile +++ b/node_server/Dockerfile @@ -1,6 +1,8 @@ FROM node:10.16-slim RUN \ + apt-get update && \ + apt-get install git -y && \ mkdir /src && \ mkdir /src/middleware @@ -18,6 +20,6 @@ COPY ./ /src/ # RUN \ # mv /src/check-auth.js /src/middleware/ -CMD node /src/server.js +CMD node --max_old_space_size=8192 /src/server.js # CMD nodemon /src/server.js # CMD tail -f /dev/null \ No newline at end of file diff --git a/node_server/app.js b/node_server/app.js index d0580da3..2e8644ba 100644 --- a/node_server/app.js +++ b/node_server/app.js @@ -7,7 +7,9 @@ const request = require('request'); const http = require('http'); const path = require('path'); const jwt = require('jsonwebtoken'); -const checkAuth = require('./middleware/check-auth'); +const checkAuth = require('./middleware/utilities').checkAuth; +const cacheMiddleware = require('./middleware/utilities').cacheMiddleware; +const memCache = require('./middleware/utilities').memCache; // const serveStatic = require('serve-static') request.debug = false; @@ -186,7 +188,7 @@ app.get('/sessions', checkAuth, (req, res) => { }); }) -app.post('/sessions', checkAuth, (req, res) => { +app.post('/sessions', checkAuth, cacheMiddleware(15*60), (req, res) => { // console.log('posting to filter session page'); request.post(flask_backend + '/v0/_q/sessionpage', { form: req.body }, function (error, httpResponse, body) { @@ -265,7 +267,7 @@ app.post('/mice', checkAuth, (req, res) => { }) -app.post('/summary', checkAuth, (req, res) => { +app.post('/summary', checkAuth, cacheMiddleware(15*60), (req, res) => { request.post(flask_backend + '/v0/_q/dailysummary', { form: req.body }, function (error, httpResponse, body) { if (error) { @@ -401,7 +403,7 @@ app.post('/plot/dateReactionTimeTrialNumberPlot', checkAuth, (req, res) => { }) }) -app.post('/plot/cluster', checkAuth, (req, res) => { +app.post('/plot/cluster', checkAuth, cacheMiddleware(15*60), (req, res) => { const timeX = new Date() // console.log('requesting cluster list at time: ', timeX, 'request: ', req.body); @@ -756,7 +758,20 @@ app.get('/plots/testPlot', (req, res, next) => { //Docker Healthcheck app.get('/version', (req, res, next) => { - res.send('Version: v1.0'); + versions = { + "DJ_API_VERSION": process.env['DJ_API_VERSION'], + "FRONTEND_VERSION": process.env['FRONTEND_VERSION'], + "NODE_API_VERSION": process.env['NODE_API_VERSION'], + "DJ_NGINX_VERSION": process.env['DJ_NGINX_VERSION'] + }; + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify(versions)); +}); + +//Node cache meta +app.get('/node-cache', checkAuth, (req, res) => { + res.setHeader("Content-Type", "application/json"); + res.send(memCache.stats()); }); // ============================================================= // diff --git a/node_server/middleware/check-auth.js b/node_server/middleware/check-auth.js deleted file mode 100644 index 9d76ece6..00000000 --- a/node_server/middleware/check-auth.js +++ /dev/null @@ -1,16 +0,0 @@ -const config = require('../config') -const jwt = require('jsonwebtoken'); - -module.exports = (req, res, next) => { - try { - // authorization token format: "Bearer aeralejlaiejrai212j1" - const token = req.headers.authorization.split(" ")[1]; - // console.log('inside authCheck, token is: ', token); - jwt.verify(token, config.jwtSecret); - next(); - } catch (error) { - res.status(401).send({message: "authorization failed at checkAuth"}); - } - - -}; \ No newline at end of file diff --git a/node_server/middleware/utilities.js b/node_server/middleware/utilities.js new file mode 100644 index 00000000..1a60a062 --- /dev/null +++ b/node_server/middleware/utilities.js @@ -0,0 +1,37 @@ +const config = require('../config') +const jwt = require('jsonwebtoken'); +let cache = require('memory-cache'); +let memCache = new cache.Cache(); + +module.exports = { + memCache: memCache, + checkAuth: (req, res, next) => { + try { + // authorization token format: "Bearer aeralejlaiejrai212j1" + const token = req.headers.authorization.split(" ")[1]; + // console.log('inside authCheck, token is: ', token); + jwt.verify(token, config.jwtSecret); + next(); + } catch (error) { + res.status(401).send({message: "authorization failed at checkAuth"}); + } + }, + cacheMiddleware: (duration) => { + return (req, res, next) => { + let key = (req.originalUrl || req.url) + '?base64=' + Buffer.from( + JSON.stringify(req.body)).toString('base64') + let cacheContent = module.exports['memCache'].get(key); + if(cacheContent){ + res.send( cacheContent ); + return + }else{ + res.sendResponse = res.send + res.send = (body) => { + module.exports['memCache'].put(key,body,duration*1000); + res.sendResponse(body) + } + next() + } + } + } +}; \ No newline at end of file diff --git a/node_server/package.json b/node_server/package.json index c197f68b..46421eb8 100644 --- a/node_server/package.json +++ b/node_server/package.json @@ -11,6 +11,7 @@ "express": "^4.16.4", "jsonwebtoken": "^8.5.1", "mathjax": "^2.7.5", + "memory-cache": "git://github.com/guzman-raphael/node-cache.git#master", "request": "^2.88.0", "zone.js": "~0.8.26" }, diff --git a/version.env b/version.env new file mode 100644 index 00000000..03db1f4b --- /dev/null +++ b/version.env @@ -0,0 +1,5 @@ +# add `-public` suffix if for public site (do not do this for nginx as it is generic) +DJ_API_VERSION=v0.1.0 +FRONTEND_VERSION=v0.1.0 +NODE_API_VERSION=v0.1.0 +DJ_NGINX_VERSION=v0.0.8 \ No newline at end of file